3

I am using Mongo C# v 2.11.5 to group users by their CategoryId. I would like the variable list_of_groups to contain a list of groups, where each group has a list of all the documents in the group, where each document contains all its individual properties. I don't want to apply any accumulator functions on any fields.

The UserGroup class is:

public class UserGroup
{
    public string gid {get; set;}
    public List<Users> users { get; set; }
}

My grouping query is

    var list_of_groups = UserCollection
        .Aggregate()
        .Group(usr => usr.CategoryId,
            g => new UserGroup { gid = g.Key, users = g.ToList() })
        .ToList<UserGroup>();

I would like to create a "list of lists", where each inner list contains all the documents of its grouping i.e. this type of structure:

List<List<UserGroup>>(
- List1<UserGroup> (list of complete documents belonging to CategoryId 1)
- List2<UserGroup> (list of complete documents belonging to CategoryId 2)
- List3<UserGroup> (list of complete documents belonging to CategoryId 3)
)

The doesn't work as the following error is generated at runtime:

System.NotSupportedException
  HResult=0x80131515
  Message=Specified method is not supported.
  Source=MongoDB.Driver

Can anyone point out what I'm missing here? I've been going in circles for a while now. Should I not being using the Group function, and maybe some other function is more appropriate since I don't want to apply any accumulator functions, and because I want each document in each group to have all its properties available?

Stack Trace:

   at MongoDB.Driver.Linq.Processors.AccumulatorBinder.GetAccumulatorArgument(Expression node)
   at MongoDB.Driver.Linq.Processors.AccumulatorBinder.TryGetAccumulatorTypeAndArgument(PipelineExpression node, AccumulatorType& accumulatorType, Expression& argument)
   at MongoDB.Driver.Linq.Processors.AccumulatorBinder.VisitPipeline(PipelineExpression node)
   at MongoDB.Driver.Linq.Expressions.PipelineExpression.Accept(ExtensionExpressionVisitor visitor)
   at MongoDB.Driver.Linq.Expressions.ExtensionExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at MongoDB.Driver.Linq.Processors.AccumulatorBinder.Bind(Expression node, IBindingContext bindingContext)
   at MongoDB.Driver.Linq.Processors.EmbeddedPipeline.EmbeddedPipelineBinder.Bind(Expression node, IBindingContext parent)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.BindEmbeddedPipeline(MethodCallExpression node)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberAssignment(MemberAssignment node)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
   at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection`1 nodes, Func`2 elementVisitor)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
   at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.Bind(Expression node, IBindingContext context, Boolean isClientSideProjection)
   at MongoDB.Driver.Linq.Processors.PipelineBindingContext.Bind(Expression node, Boolean isClientSideProjection)
   at MongoDB.Driver.Linq.Processors.PipelineBindingContext.Bind(Expression node)
   at MongoDB.Driver.Linq.Translators.AggregateGroupTranslator.BindGroup[TKey,TDocument,TResult](PipelineBindingContext bindingContext, Expression`1 groupProjector, IBsonSerializer`1 parameterSerializer, Expression keySelector)
   at MongoDB.Driver.Linq.Translators.AggregateGroupTranslator.Translate[TKey,TDocument,TResult](Expression`1 idProjector, Expression`1 groupProjector, IBsonSerializer`1 parameterSerializer, IBsonSerializerRegistry serializerRegistry, ExpressionTranslationOptions translationOptions)
   at MongoDB.Driver.GroupExpressionProjection`3.Render(IBsonSerializer`1 documentSerializer, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.PipelineStageDefinitionBuilder.<>c__DisplayClass19_0`2.<Group>b__0(IBsonSerializer`1 s, IBsonSerializerRegistry sr)
   at MongoDB.Driver.DelegatedPipelineStageDefinition`2.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.AppendedStagePipelineDefinition`3.Render(IBsonSerializer`1 inputSerializer, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.MongoCollectionImpl`1.Aggregate[TResult](IClientSessionHandle session, PipelineDefinition`2 pipeline, AggregateOptions options, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.<>c__DisplayClass19_0`1.<Aggregate>b__0(IClientSessionHandle session)
   at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSession[TResult](Func`2 func, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.Aggregate[TResult](PipelineDefinition`2 pipeline, AggregateOptions options, CancellationToken cancellationToken)
   at MongoDB.Driver.CollectionAggregateFluent`2.ToCursor(CancellationToken cancellationToken)
DalhousieDuck
  • 329
  • 1
  • 16
  • 1
    What do you expect from the `.Aggregate()`? Here is the doc on this method. https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.aggregate?view=net-5.0. – Drag and Drop Jan 06 '21 at 13:51
  • Does this answer your question? [Using Linq to group a list of objects into a new grouped list of list of objects](https://stackoverflow.com/questions/2697253/using-linq-to-group-a-list-of-objects-into-a-new-grouped-list-of-list-of-objects) – Drag and Drop Jan 06 '21 at 13:51
  • @DragandDrop Not sure we're considering the same Aggregate() method- I'm using the IMongoCollectionExtensions.Aggregate which returns an IAggregateFluent from https://mongodb.github.io/mongo-csharp-driver/2.8/apidocs/html/M_MongoDB_Driver_IMongoCollectionExtensions_Aggregate__1.htm – DalhousieDuck Jan 06 '21 at 14:52
  • Ma bad. Never used that one. I missed the tag aggregation-framework, that was a good hint of it beeing a framework specific method. For my defence I often see code with naked Aggregate not related to that framework, as it's the first method in the Crtl space suggestion. It's sometimes use as a place holder or go-get-it method. – Drag and Drop Jan 07 '21 at 08:36

1 Answers1

4

The problem you're facing here is that everything that's within .Group() method should get translated into corresponding MongoDB / Aggregation Framework operator and obviously there's no .ToList() counterpart. Therefore, there's no path forward with LINQ if you want to return a list of documents and you have to use Aggregation Framework's syntax directly and take advantage of $$ROOT variable:

public class UserGroup
{
    [BsonElement("_id")]
    public string gid { get; set; }
    public List<User> users { get; set; }
}

var groupDef = new BsonDocument()
{
    { "_id", "$CategoryId" },
    { "users", new BsonDocument(){ { "$push", "$$ROOT" } } }
};

var res = collection.Aggregate().Group<UserGroup>(groupDef).ToList();
mickl
  • 48,568
  • 9
  • 60
  • 89
  • How can this example be adapted, if only the groups containing more than n users (for example 2) should be returned? Is it possible to write the query without using the BsonDocument (linq or fluentaggregate style) using the new linq3 provider from the mongodb c# driver 2.14? – RickyTad Dec 16 '21 at 10:22
  • @RickyTad please open another question and describe your case – mickl Dec 16 '21 at 12:34