I'm trying to translate the following MongoDb command to its equivalent in MongoDriver for .Net Core:
db.test.aggregate([
{
$project: {
values: {
$map: {
input: { $objectToArray: "$foo" },
as: "val",
in: "$$val.v.value"
}
}
}
}
]);
But I'm having a bad time trying to understand the docs since I see a lot of references to types that my compiler cannot find (like AggregateArgs). What would be the best way of expressing it using C# syntax?
Try this:
var projectStage = BsonDocument.Parse(#"
{
'values' : {
'$map' : {
'input' : {
'$objectToArray' : '$foo'
},
'as': 'val',
'in': '$$val.v.value'
}
}}");
var coll = new MongoClient().GetDatabase("db").GetCollection<BsonDocument>("coll");
var res = coll
.Aggregate()
.Project(projectStage)
.ToList();
The generated projection query will be:
{
"$project": {
"values": {
"$map": {
"input": {
"$objectToArray": "$foo"
},
"as": "val",
"in": "$$val.v.value"
}
}
}
}
Unfortunately, $objectToArray currently is not supported via the typed way. You can find how similar queries with typed way and LINQ can look like here: https://github.com/mongodb/mongo-csharp-driver/blob/master/tests/MongoDB.Driver.Tests/Linq/Translators/AggregateProjectTranslatorTests.cs
Related
Is there any way to use Filters in C# and translate this mongo query?
{'EmailData.Attachments.Files': {$all: [{Name: 'a.txt'},{Name: 'b.txt'},{Name:'c.txt'}], $size: 3}}
my data model is like:
{
"_id": ObjectId("5f0a9c07b001406068c073c1"),
"EmailData" : [
{
"Attachments" : {
"Files" : [
{
"Name" : "a.txt"
},
{
"Name" : "b.txt"
},
{
"Name" : "c.txt"
}
]
}
}
]
}
I have something like this in my mind:
var Filter =
Builders<EmailEntity>.Filter.All(s => s.EmailData????);
or something like:
var Filter =
Builders<EmailEntity>.Filter.ElemMatch(s => s.EmailData???)
I was wondering is there any way in the above filter to use All inside ElemMatch?
The difficulty here is that EmailData.Attachments.Files is an array within another array so C# compiler will get lost when you try to use Expression Trees.
Thankfully there's another approach when you need to define a field using MongoDB .NET driver. You can take advantage of StringFieldDefinition<T> class.
Try:
var files = new[] { new FileData(){ Name = "a.txt"}, new FileData() { Name = "b.txt" }, new FileData() { Name = "c.txt" } };
FieldDefinition<EmailEntity> fieldDef = new StringFieldDefinition<EmailEntity>("EmailData.Attachments.Files");
var filter = Builders<EmailEntity>.Filter.And(
Builders<EmailEntity>.Filter.All(fieldDef, files),
Builders<EmailEntity>.Filter.Size(fieldDef, 3));
var result= collection.Find(filter).ToList();
I'm trying to build aggregate pipeline with value search according to the value with the C# mongo driver.
The first stage would to filter where KeyA is "A" and then the second stage would be to do the search where I have a certain list of keys that I need to go over and compare their value with a regex, according to documentation the regex only checks strings and I need to check any type that the value can hold.
I'm adding a model and code examples for what I have done so far but couldn't get to the expected end result, any help would be appriciated.
Model for example:
{
"_id" : ObjectId(),
"KeyA" : "A",
"KeyB" : NumberInt(5),
"KeyNestedObj" : [
{
"KeyC" : "C"
},
{
"KeyD" : "D"
},
{
"KeyE" : NumberInt(5)
}
]
}
Mongo shell query:
db.MongoTests.aggregate([{ "$match": { "KeyA": "A" }},
{ "$match": { "$or": [{ "KeyNestedObj": { "$elemMatch": { "KeyC": /^.*?Regex.*?$/ } } }, { "KeyNestedObj": { "$elemMatch": { "KeyE": /^.*?Regex.*?$/ }}}]}}])
C# query build:
var searchableFields = new List<SearchableField>
{
new SearchableField{ Field = "KeyC" },
new SearchableField{ Field = "KeyE" }
};
var searchableFieldsFilter = new List<FilterDefinition<Model>>();
var commonSearchBuilder = Builders<Model>.Filter;
var nestedObjSearchBuilder = Builders<KeyNestedObj>.Filter;
var searchValueRegex = new BsonRegularExpression($"^.*?{searchValue}.*?$");
foreach (var searchableField in searchableFields)
{
var searchNestedField =
commonSearchBuilder.ElemMatch(x => x.KeyNestedObj,
nestedObjSearchBuilder.Regex(searchableField.Field, searchValueRegex));
searchableFieldsFilter.Add(searchNestedField);
}
return Builders<ActivityDataModel>.Filter.And(searchableFieldsFilter);
Code aggregation query:
var renderedSearchQuery = searchStage.Render(mongoContext.Data.DocumentSerializer,
mongoContext.Data.Settings.SerializerRegistry);
var results= mongoContext.Data.Aggregate().Match(filterQuery).AppendStage<Model>(renderedSearchQuery).ToListAsync();
I have a mongo collection, shown below :
I am using C#, ASP .Net Core 2.0 MongoDB 3.6
I need to get the count of those TopicVideo Object, which have videolinks value(at least not empty)
Count(distinct VideoLink) From TopicVideo Where videolink is not null
of it will be enough for me to check if it starts with http or not.
Here is a code, I have tried but not working.
var teahcersCollection = database.GetCollection<BsonDocument>("Teachers");
return teahcersCollection.Count(Builders<BsonDocument>.Filter.Regex("TopicVideo.VideoLink'", "/http.*/"));
Again, To clear, how to get the count of videoLink in this scenario?
The Other thing
And another thing, I am struggling too much with this type of queries and I want to query from the collection. please refer me any good step by step detailed documentation if you think available.
You can use the following aggregation query:
var teachersCollection = database.GetCollection<Teacher>("Teachers");
var regexFilter = Builders<BsonDocument>.Filter.Regex("TopicVideo.VideoLink", "/^http/");
var httpBsonCount = teachersCollection.Aggregate()
.Unwind(t => t.TopicVideo)
.Match(regexFilter)
.Group(new BsonDocument { { "_id", "null" }, { "count", new BsonDocument { { "$sum", 1 } } } })
.ToList();
int httpCount = 0;
if (httpBsonCount.Any())
httpCount = httpBsonCount.First()["count"].AsInt32;
Once executed the query, httpCount will contain your expected value.
In the mongo console, the query looks like:
db.Teachers.aggregate([
{ $unwind: "$TopicVideo"},
{ $match: { "TopicVideo.VideoLink": /^http/ } },
{ $group: { _id: null, count: { $sum : 1 } } }
]);
In order to learn about how to query MongoDB through C# driver I suggest you to follow the free M101N course.
I have the following working aggregate statement in MongoDB:
db.Forms.aggregate( [
{ $match: { Id: { $nin: db.Forms.distinct("Id", {"Status": "DELETE"}, {Id: 1}) } }},
{ $group: { _id: "$Id", lastModifiedId: { $last: "$_id" } } }
]).result
The statement selects all Forms that do not have a delete record on the Forms table and returns their last modified rows.
What I would like to know is: how do I write the aggregate statement above using the MongoDB C# driver? This is what I have so far:
var aggregate = _forms.Aggregate()
//.Match(new BsonDocument { { "Id", new BsonDocument("$nin", "db.Forms.distinct(\"Id\", {\"Status\": \"DELETE\"}, {Id: 1})") } })
.Group(new BsonDocument { { "_id", "$Id" }, { "lastModifiedId", new BsonDocument("$last", "_id") } });
var aggregateResults = aggregate.ToListAsync().Result.ToList();
I just need to get the Match statement sorted out.
I've looked at a few articles about aggregation and I understand the concept (I think), but the "db.Forms.distinct" is where I'm having issues and none of the articles that I found handled this scenario.
If there's a better way of doing this, please let me know.
Thank you!
I am doing a NearSphere query with the C# MongoDB driver 2.0 and it works fine.
The results are ordered by distance automatically but I would like to get that distance back for each of the search results to be able to display it back.
I found this post that says how to do it for the old version of the driver Retrieving the Distance "dis" result from a Near query but didn't manage to find how to do it with the new drivers.
This is my code:
var collection = database.GetCollection<MyType>("myTypes");
var locFilter = Builders<MyType>.Filter.NearSphere(x => x.GeoLocation, criteria.Long, criteria.Lat, criteria.RadiusInMiles/3963.2);
var results = await collection.Find(locFilter).ToListAsync();
I guess I have to do something before calling ToList on the IFindFluent result ?
Any help ?
Thanks a lot
The C# MongoDB Driver lacks of some operations but the reality there's not any restriction querying the database. The methods Project, Match and so forth are just helpers to build a PipeLine that's exactly a BsonDocument like any other.
I had to query the database from C# using a similar approach to yours:
db.mycoll.aggregate(
[ { $geoNear :
{ near : { type : "Point", coordinates : [-34.5460069,-58.48894900000001] },
distanceField : "dist.calculated", maxDistance : 100,
includeLocs : "dist.location",
num : 5, spherical : true }
} ,
{ $project : {_id: 1, place_id:1, name:1, dist:1} }
] ).pretty()
As you know there's not a geoNear method to build it in the driver, so you can just create the BsonDocument.
To show you that everything can be built in that way find below a sample query from C# not using the project clausule either, I built it from the BsonDocument. You can push BsonDocument's in the pipeline as you wish.
var coll = _database.GetCollection<UrbanEntity>("mycoll");
var geoNearOptions = new BsonDocument {
{ "near", new BsonDocument {
{ "type", "Point" },
{ "coordinates", new BsonArray {-34.5460069,-58.48894900000001} },
} },
{ "distanceField", "dist.calculated" },
{ "maxDistance", 100 },
{ "includeLocs", "dist.location" },
{ "num", 5 },
{ "spherical" , true }
};
var projectOptions = new BsonDocument {
{ "_id" , 1 },
{ "place_id", 1 },
{ "name" , 1 },
{ "dist", 1}
};
var pipeline = new List<BsonDocument>();
pipeline.Add( new BsonDocument { {"$geoNear", geoNearOptions} });
pipeline.Add( new BsonDocument { {"$project", projectOptions} });
using(var cursor = await coll.AggregateAsync<BsonDocument>(pipeline)) {
while(await cursor.MoveNextAsync()) {
foreach (var doc in cursor.Current) {
// Here you have the documents ready to read
}
}
}
I hope it helps.