MongoDb C# Typed Aggregations with Group Unwind an

2020-03-26 12:07发布

问题:

I have a collection like this:

[{
  "_id": 1,
  "OtherProperties": 100
  "PersonInventory": [{
    "FirstName": "Joe",
    "MiddleName": "Bob",
    "LastName": "Blogs",
    "PersonId": 1
  }]
},{
  "_id": 2,
  "OtherProperties": 1005
  "PersonInventory": [{
    "FirstName": "Joe",
    "MiddleName": "Bob",
    "LastName": "Blogs",
    "PersonId": 1
  }]
}]

And I am trying to select all the unique persons in the root docs using here newer type inference mongodb c# driver syntax.

Tried this so far but getting errors saying the method is not available (i think this is to do with the grouping), can anyone show me where I am going wrong?

collection.Aggregate()
  .Match(m => m.PersonInventory.Count > o)
  .Unwind<RootDoc, Person>(t => t.PersonInventory)
  .Group(k => k.PersonId, g => g.First())
  .ToListAsync();

Models:

public class Person
{
  [BsonId]
  public long PersonId { get; set; }
  public string FirstName { get; set; }
  public string MiddleName { get; set; }
  public string LastName { get; set; }
}

public class RootDoc
{
  public long Id{ get; set; }
  //more props
  public IList<Person> PersonInventory { get; set; }
}

FYI the error message is Specified method is not supported..

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.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 MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(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.<AggregateAsync>d__22`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MongoDB.Driver.MongoCollectionImpl`1.<UsingImplicitSessionAsync>d__101`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at MongoDB.Driver.IAsyncCursorSourceExtensions.<ToListAsync>d__16`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at App.Application.Services.CacheSyncService.<GetAllCrewAsync>d__9.MoveNext() in C:\Users\gdp\Sites\app\Services\CacheSyncService.cs:line 77
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at App.Application.Services.Sync.ReferenceDataSyncService.<ResolveCrewReferences>d__6.MoveNext() in C:\Users\gdp\Sites\app\Services\Sync\ReferenceDataSyncService.cs:line 46
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at App.API.Controllers.DataSyncController.<ResolveCrewReferences>d__3.MoveNext() in C:\Users\gdp\Sites\app\Controllers\DataSyncController.Triggers.cs:line 87
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.TaskHelpersExtensions.<CastToObject>d__1`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__6.MoveNext()

回答1:

I did some digging on the mongodb Jira pages, it looks like there isn't support for the variable $$ROOT, so the above queries are not supported.



回答2:

this can be easily achieved with the IMongoQueryable interface like shown below

using MongoDB.Entities;
using System.Linq;

namespace StackOverflow
{
    public class RootDoc : Entity
    {
        public Person[] PersonInventory { get; set; }
    }

    public class Person
    {
        public long PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

    public class Program
    {
        private static void Main(string[] args)
        {
            new DB("test");

            (new[] {
                new RootDoc{
                    PersonInventory = new[]{
                        new Person{ PersonId = 1, FirstName = "first", LastName="person"},
                        new Person{PersonId = 2, FirstName = "second", LastName="person"}
                    }
                },
                new RootDoc{
                    PersonInventory = new[]{
                        new Person { PersonId = 2, FirstName = "second", LastName = "person" } }
                }
            }).Save();

            var uniquePersons = DB.Queryable<RootDoc>()
                                  .Where(r => r.PersonInventory.Any())
                                  .SelectMany(r => r.PersonInventory)
                                  .Distinct()
                                  .ToArray();
        }
    }
}

above code uses my library MongoDB.Entities for brevity. just replace DB.Queryable<RootDoc>() with collection.AsQueryable() for official driver.