C#: Create Index For An Attribute In Embedded Document - c#

I have following document in my MongoDB database:
{
"_id":1,
"user_name":"John Doe",
"addresses":[
{
"_id":null,
"geolocation":null,
"city":"Toronto"
},
{
"_id":null,
"geolocation":null,
"city":"Canada"
}
]
}
I want to create an index for the attribute addresses.city in my C# code so that I can do a text based search later. How can I achieve this?
By the way, I have implemented the following code to achieve this but it seems it doesn't work while running the query:
public void createIndexes() {
try {
var indexOptions = new CreateIndexOptions();
var indexKeys = Builders < Users> .IndexKeys.Text(_ => _.Addresses);
var indexModel = new CreateIndexModel < Users> (indexKeys, indexOptions);
var collection = _mongoDb.GetCollection < Users> ("users");
collection.Indexes.CreateOneAsync(indexModel);
} catch (Exception ex) {
throw;
}
}
The code executing the query:
//Creates connection with the mongodb
private MongoDbContext db = new MongoDbContext();
...
...
//Initializes the search term
string searchTerm = "Cana";
//Executes the query
var builder = Builders<Users>.Filter;
FilterDefinition<Users> filter;
filter = builder.Text(searchTerm);
var results = await db.Users.Find(filter).ToListAsync();
I'm using this MongoDB driver

Related

How to get total count from a filtered FeedIterator in Cosmos DB

I have the following code to filter some data in a Cosmos DB:
Container container = await service.GetContainer(containerName, partitionKeyA);
using (FeedIterator<T> resultSet = container.GetItemQueryIterator<T>(
queryDefinition: GetComplexSQLQueryDefinition(),
paginationInfo.Token,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey(partitionKey),
MaxItemCount = 10
}
))
{
FeedResponse<T> response = await resultSet.ReadNextAsync();
//get total count
int totalCount = -1;
}
The query can yield many records, hence I need the pagination.
Unfortunately I need the total count of the items - and it can vary according to the filtering.
According to this answer, I have only 2 options:
I create a second SQL query that uses an SQL select command to count the records - then query the database again to select the actual records:
var query = new QueryDefinition("SELECT value count(1) FROM c WHERE c.tenantId = #type");
query.WithParameter("#type", '5d484526d76e9653e6226aa2');
var container = client.GetContainer("DatabaseName", "CollectionName");
var iterator = container.GetItemQueryIterator<int>(query);
var count = 0;
while (iterator.HasMoreResults)
{
var currentResultSet = await iterator.ReadNextAsync();
foreach (var res in currentResultSet)
{
count += res;
}
}
Console.WriterLine($"The first count is: {count}");
I translate my complex SQL query to LINQ and use its Count method:
//should be formatted as code
var count = container.GetItemLinqQueryable(true)
.Count(item => item.tenantId.Equals('5d484526d76e9653e6226aa2'));
Both seems quite cumbersome for such a simple task so I wonder if there is any better or more effective approach.
What else could I try?
Here's roughly the code I've used to perform count operations with .NET SDK v3 based on an arbitrary QueryDefinition. There are a few aspects not shown like the full logic of deserialization with System.Text.Json, but hopefully the gist comes across. The idea is to provide a query with base:
SELECT VALUE COUNT(1) FROM c
To get a single document as result which contains the result count;
public async Task<int?> CountAsync(QueryDefinition query, string partitionKey)
{
var options = new QueryRequestOptions() { PartitionKey = new(partitionKey), MaxItemCount = 1 };
int? count = null;
using var resultSet = cosmosService.DataContainer.GetItemQueryStreamIterator(query, requestOptions: options);
while (resultSet.HasMoreResults)
{
using var response = await resultSet.ReadNextAsync();
if (response.IsSuccessStatusCode)
{
// Deserialize response into CosmosResponse<int>
var deserializeResult = await FromJsonStream<CosmosResponse<int>>(response.Content);
if (!deserializeResult.HasSuccessValue(out CosmosResponse<int>? responseContent))
{
return null; // Or some failure
}
if (responseContent.Documents.Any())
{
count = responseContent.Documents[0];
break;
}
else
{
return null;// Or some failure
}
}
else // Unexpected status. Abort processing.
{
return null;// Or some failure
}
}
return count;
}
Helper class to deserialize the response:
public class CosmosResponse<T>
{
[JsonPropertyName("Documents")]
public IReadOnlyList<T> Documents { get; set; } = Array.Empty<T>();
[JsonPropertyName("_count")]
public int Count { get; set; }
}

How to combine a distinct query with projection with C# MongoDB driver

I try to combine a projection and a distinct with the MongoDB driver but don't get anywhere...
I have:
var coll = db.GetCollection<Vat>(CommonConstants.VatCodeCollection);
// I like to combine in one statement:
var result = coll.Distinct<DateTime>("ValidSince", filter).ToList();
var projection = Builders<Vat>.Projection.Expression(x => new VatPeriod { ValidSince = x.ValidSince });
So at the end I like to get a List<VatPeriod> as a result of one statement. Of course I could do something like
var coll = db.GetCollection<Vat>(CommonConstants.VatCodeCollection);
List<VatPeriod> vatPeriods = null;
try
{
var result = coll.Distinct<DateTime>("ValidSince", filter).ToList();
if (result.Count > 0)
{
vatPeriods = new List<VatPeriod>(result.Count);
foreach (var dateTime in result)
{
vatPeriods.Add(new VatPeriod() {ValidSince = dateTime});
}
}
return vatPeriods;
}
catch .....
in my repository class, but I would prefer to do everything on the Mongo server. Any idea if and how this is possible?

How to build a conditional query in MongoDB C#

I am having trouble building a conditional query using C# MongoDB driver. Whenever I run the code below, I get an empty list. Any help will be greatly appreciated.
Here is my function
public async void searchBook()
{
Book book = new Book();
IMongoDatabase mdb = MongoDBConnectionManager.ConnectToMongoDB();
var query = new BsonDocument();
if (queryString.ContainsKey("Title"))
{
query.Add("Title", queryString["Title"]);
}
if (queryString.ContainsKey("ISBN"))
{
query.Add("Isbn", queryString["ISBN"]);
}
if (queryString.ContainsKey("Author"))
{
query.Add("Author", queryString["Author"]);
}
if (queryString.ContainsKey("Publisher"))
{
query.Add("Publisher", queryString["Publisher"]);
}
var collection = mdb.GetCollection<Book>("Book");
var sort = Builders<Book>.Sort.Ascending("Title");
if(query.ElementCount > 0)
{
var list = await collection.Find(query).Sort(sort).ToListAsync();
dt = ConvertToDataTable(list);
BindGridView(dt);
}
else
{
var list = await collection.Find(Builders<Book>.Filter.Empty).Sort(sort).ToListAsync();
dt = ConvertToDataTable(list);
BindGridView(dt);
}
}
You can use IMongoCollection to get your collection and then use AsQueryable
var query = collection.AsQueryable();
if (!string.IsNullOrEmpty(entity.Name))
query = query.Where(p => p.Name.Contains(entity.Name));
if (!string.IsNullOrEmpty(entity.Description))
query = query.Where(p => p.Description.Contains(entity.Description));
var YourList=query.ToList();

C# mongoDB wont update array in database

I want to update array in my mongoDB collection. Posting new document works just fine, but it won't update array. Structure of my document is:
var document = new BsonDocument
{
{ "post_title", model.Title },
{ "post_text", model.Text },
{ "post_author", model.Author },
{ "post_picture", model.Picture },
{ "post_comments", new BsonArray() },
};
And my update function is:
[HttpPost]
[Route("api/PostInfo/{Comment}")]
public async Task Post(Comment comment)
{
try {
BsonObjectId oldId = new BsonObjectId(new ObjectId(comment.id.ToString()));
var mongoDbClient = new MongoClient("mongodb://127.0.0.1:27017");
var mongoDbServer = mongoDbClient.GetDatabase("nmbp");
var collection = mongoDbServer.GetCollection<PostInfo>("post");
var filter = Builders<PostInfo>.Filter.Eq(e => e._id, oldId);
var update = Builders<PostInfo>.Update.Push("post_comments", comment.comment);
await collection.FindOneAndUpdateAsync(filter, update);
var test = oldId.GetType();
}
catch
{
}
}
When debugging, i can see that post controller is triggered, and comment values are correct, but when I take a look in database, value of "post_comments" array is empty. No error is thrown in catch block. Am I doing something wrong?
It looks like the problem was in this line:
var filter = Builders<PostInfo>.Filter.Eq(e => e._id, oldId);
The correct one should look like this:
var filter = Builders<PostInfo>.Filter.Eq("_id", oldId);

Elastic search with Nest

I am working on below code, and what I want to do is query by object itself.
For example: I have a search form, that populates objects fields such as below. Then what I want to do is to search Elastic search based on whatever user filled the form with.
ie: below, I want to query the index by searchItem object. How can I do it easily?
class Program
{
static void Main(string[] args)
{
var p = new Program();
var item1 = new Announcement() {Id=1, Title = "john", ContentText = "lorem", Bar = false, Num = 99, Foo = "hellow"};
//p.Index(item1, "add");
var searchItem = new Announcement() {Title="john",Num=99};
ElasticClient.Search<Announcement>();
Console.Read();
}
public void Index(Announcement announcement, String operation)
{
var uriString = "http://localhost:9200";
var searchBoxUri = new Uri(uriString);
var settings = new ConnectionSettings(searchBoxUri);
settings.SetDefaultIndex("test");
var client = new ElasticClient(settings);
if (operation.Equals("delete"))
{
client.DeleteById("test", "announcement", announcement.Id);
}
else
{
client.Index(announcement, "test", "announcement", announcement.Id);
}
}
private static ElasticClient ElasticClient
{
get
{
try
{
var uriString = "http://localhost:9200";
var searchBoxUri = new Uri(uriString);
var settings = new ConnectionSettings(searchBoxUri);
settings.SetDefaultIndex("test");
return new ElasticClient(settings);
}
catch (Exception)
{
throw;
}
}
}
}
You can't :)
NEST cannot infer how to best query only based on a partially filled POCO. Should it OR or AND should it do a nested term query or a term query wrapped in a has_child? You catch my drift.
Nest does have a slick feature called conditionless queries that allow you the write out to entire query like so:
ElasticClient.Search<Announcement>(s=>s
.Query(q=>
q.Term(p=>p.Title, searchItem.Title)
&& q.Term(p=>p.Num, searchItem.Num)
//Many more queries use () to group all you want
)
)
When NEST sees that the argument passed to Term is null or empty it simply wont render that part of the query.
Read more here on how this feature works http://nest.azurewebsites.net/concepts/writing-queries.html

Categories