I'm trying to type-safe group documents by multiple properties from a collection. I figured out that the problem is not the grouping by multiple properties itself but that the key is an object (new { }
) instead of a string.
This works:
collection.Aggregate().Group(x => x.Name, x => new { Name = x.Key, Count = x.Sum(s => 1) }).ToList();
This does not work:
collection.Aggregate().Group(x => new { Name = x.Name }, x => new { Name = x.Key.Name, Count = x.Sum(s => 1) }).ToList();
The Error is:
MongoDB.Driver.MongoCommandException: 'Command aggregate failed: The field 'Name' must be an accumulator object.'
When I convert the query that doesn't work to a string I get the following:
aggregate([{ "$group" : { "_id" : { "Name" : "$Name" }, "Name" : "$_id.Name", "Count" : { "$sum" : 1 } } }])
I guess the problem here is this part "Name" : "$_id.Name"
. How do I fix this problem?
I'm currently using version 2.8.0 of the mongodb driver.
Actually the driver behaves correctly here but what you're experiencing is the limitation of $group pipeline stage in Aggregation Framework. The documentation states that:
The output documents contain an _id field which contains the distinct group by key. The output documents can also contain computed fields that hold the values of some accumulator expression grouped by the $group’s _id field
So in your case you're trying to refer to _id
field without any accumulator.
From the logical perspective your queries appear to be similar. First one gets translated into:
{
"$group" : {
"_id" : "$Name",
"Count" : {
"$sum" : 1
}
}
}
Second one would return the same data but you have an object as a grouping key. To fix that in C# you need to introduce .First()
expression which will be translated into $first, so running:
Col.Aggregate()
.Group(
x => new { Name = x.Name },
x => new { Name = x.First().Name, Count = x.Sum(s => 1) })
.ToList();
will run below query on the database:
{
"$group" : {
"_id" : {
"Name" : "$Name"
},
"Name" : {
"$first" : "$Name"
},
"Count" : {
"$sum" : 1
}
}
}
It works but logically it returns the same data as first aggregation