Присоединение MongoDB LINQ к свойству типа свойства коллекции - PullRequest
2 голосов
/ 28 апреля 2020

У меня есть следующие классы, которые сохраняются через MongoDb:

public class TypeA
{
    public string Name { get; set; }
    public List<TypeB> Children { get; set; }
}

public class TypeB
{
    public string Colour { get; set; }
}

И я объединяю две коллекции TypeA следующим образом:

IQueryable<TypeA> collection1 = GetCollection();
IQueryable<TypeA> collection2 = GetCollection();

var query = from a in collection1
            join b in collection2
                on a.Name equals b.Name
            select new { a.Name };

var list = query.ToList();

Как я могу сделать подобное что-нибудь, но на дочерних коллекциях свойств для свойства Color?

Я пробовал что-то вроде следующего, но драйвер MongoDb Linq явно не любит операторы SelectMany в предложении соединения:

IQueryable<TypeA> collection1 = GetCollection();
IQueryable<TypeA> collection2 = GetCollection();

var query = from a in collection1.SelectMany(a => a.Children)
            join b in collection2.SelectMany(b => b.Children)
                on a.Colour equals b.Colour
            select new { a.Colour };

var list = query.ToList();

1 Ответ

1 голос
/ 29 апреля 2020

LINQ не является путем для такой операции, так как драйвер не сможет преобразовать такой синтаксис в платформу агрегации MongoDB.

Этап конвейера, который вам нужно использовать, - $ lookup with custom конвейер , который позволяет указать пользовательское условие "соединения". В этом случае вам потребуется либо $ setEquals , либо $ setIsSubset . Ваш код C# может выглядеть так:

var letDef = BsonDocument.Parse("{ colA_Children: '$Children' }");

var filter = new BsonDocumentFilterDefinition<TypeA>(BsonDocument.Parse("{ $expr: { $setEquals: [ '$$colA_Children', '$Children' ] } }"));

var pipeline = new PipelineStagePipelineDefinition<TypeA, TypeA>(
    new IPipelineStageDefinition[]
    {
        PipelineStageDefinitionBuilder.Match(filter),
    });

var fieldDef = new ExpressionFieldDefinition<AggResult, List<TypeA>>(f => f.Matched);

var results = col1.Aggregate()
    .Lookup<TypeA, TypeA, List<TypeA>, AggResult>(
        foreignCollection: col2,
        let: letDef,
        lookupPipeline: pipeline,
        @as: fieldDef
    ).ToList();

где:

[BsonIgnoreExtraElements]
public class AggResult : TypeA
{
    public List<TypeA> Matched { get; set; }
}
...