首页 > 解决方案 > 编写查询以从特定文档结构的 mongo 获取信息

问题描述

想象一下我有这个文档结构:

DocumentOne
{
  string Id { get; set; };
  string InnerId { get; set; }
  DocumentTwo[] InnerDocuments { get; set; }
}

DocumentTwo
{
  string Id { get; set; }
  string AnotherField { get; set; }
}

我尝试DocumentOne.InnerId != DocumentTwo.Id使用 mongodb 驱动程序编写查询以在 .net 中按条件过滤文档。

我试着用

Builder<DocumentOne>.Filter.ElemMatch(x => x.InnerDocuments, y => y.Id != ???)

但我无法在此查询中访问 InnerId(问号)

如果我尝试使用 Fluent Syntax 之类的

database.Find(x => x.InnerDocuments.Contains(y => y.Id != x.InnerId)) 

或者

database.Find(!x => x.InnerDocuments.Any(y => y.Id != x.InnerId))

我收到来自驱动程序的错误消息。

我需要如何重写这个查询?

标签: c#.netmongodb

解决方案


有两种方法可以做到这一点,通过聚合或使用带有表达式的查找。

我们将看一下聚合,因为对我来说它更容易一些。

所以从你说的开始,我们将有 2 个 MongoDB 模型


    public class DocumentOne
    {
        public string Id { get; set; }
        public string InnerId { get; set; }
        public DocumentTwo[] InnerDocuments { get; set; }
    }

    public class DocumentTwo
    {
        public string Id { get; set; }
        public string AnotherField { get; set; }
    }

然后,当我们展开内部文档时,我们需要第二个投影来跟踪后面的事情:


    public class DocumentOneProjection
    {
        public string Id { get; set; }
        public string InnerId { get; set; }
        public DocumentTwo InnerDocuments { get; set; }
    }

所以我们会把一些数据扔到 MongoDB 中来玩

var client = new MongoClient();
var database = client.GetDatabase("test");
var collection = database.GetCollection<DocumentOne>("documents");

await database.DropCollectionAsync(collection.CollectionNamespace.CollectionName);

await collection.InsertManyAsync(new[]
{
    new DocumentOne()
    {
        Id = "1", InnerId = "10", InnerDocuments = new[]
        {
            new DocumentTwo()
            {
                Id = "11"
            }
        }
    },
    new DocumentOne()
    {
        Id = "2", InnerId = "20", InnerDocuments = new[]
        {
            new DocumentTwo()
            {
                Id = "20"
            }
        }
    },
    new DocumentOne()
    {
        Id = "3", InnerId = "30", InnerDocuments = new[]
        {
            new DocumentTwo()
            {
                Id = "30"
            },
            new DocumentTwo()
            {
                Id = "31"
            }
        }
    }
});

然后我们就可以创建一个聚合查询

var items = await collection.Aggregate()
    .Unwind(x => x.InnerDocuments)
    .AppendStage<BsonDocument>(
        @"{ $addFields: { matchInner: { $cmp: [ ""$InnerId"", ""$InnerDocuments._id"" ] } } }")
    .Match("{ matchInner: { $ne : 0 } }") // 0 if the two values are equivalent.
    .AppendStage<DocumentOneProjection>(
        @"{ $unset: ""matchInner"" }")
    .ToListAsync();

此查询从展开所有内部文档开始,这将为数组中的每个文档创建一个文档(https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/

然后它创建一个新字段,我们稍后将创建一个匹配项,这使用比较($cmp - https://docs.mongodb.com/manual/reference/operator/aggregation/cmp/

然后我们只匹配新字段,然后执行查询以取回文档。


推荐阅读