MongoDB c# 使用定义构建器检索文档中数组中的所有匹配元素

Sub*_*sta 4 c# mongodb mongodb-.net-driver asp.net-core

我有一个结构看起来像这样的文档,带有嵌套的子文档

{  
   "_id":ObjectId("50419077c2e6a1e18a489a0f"),
   "user":"Jone Doe",
   "fooArray":[  
      {  
         "plot":"circle",
         "color":"yellow",
      },
      {  
         "plot":"circle",
         "color":"red",
      },
      {  
         "plot":"square",
         "color":"green",
      }
   ]
}
Run Code Online (Sandbox Code Playgroud)

我想在这个具有圆形图的文档中检索 fooArray 中的所有匹配元素。

这是我试过的

var filter = FilterBuilder.filter.Eq(doc => doc.User, User);
var projection = ProjectionBuilder
                .Exclude(doc => doc.Id)
                .Exclude(doc => doc.User)
                .Include(doc => doc.FooArray)
                .ElemMatch(x => x.FooArray, y => y.Plot == "circle");

var definition = new OperationDefinitions<ShapeDocument> { Filter = filter };
            return await Performer.Perform(definition, async (def, collection) =>
            {
                var findResult = collection.Find(def.Filter).Project(projection);

                var result = await findResult.SingleOrDefaultAsync();
            });
Run Code Online (Sandbox Code Playgroud)

这就是我得到的

{  
   "fooArray":[  
      {  
         "plot":"circle",
         "color":"yellow",
      }
   ]
}
Run Code Online (Sandbox Code Playgroud)

但它只给了我第一个匹配元素,而不是所有绘图等于圆的元素

{  
   "fooArray":[  
      {  
         "plot":"circle",
         "color":"yellow",
      },
      {  
         "plot":"circle",
         "color":"red",
      }
   ]
}
Run Code Online (Sandbox Code Playgroud)

我确实阅读了提到的 mongodb 文档

" $elemMatch 运算符将查询结果中的字段内容限制为仅包含与 $elemMatch 条件匹配的第一个元素。"

不太确定如何实现这一目标!

Gre*_*ley 5

这个问题并没有完全描述用例,所以我根据一些假设提出了一些潜在的选项供您探索,特别是它们依赖于 LINQ 可用并且一次定位一个文档(并且您可能不想要比实际需要更多的代码):

1)你所拥有的变化。使用find带有投影和 LINQ 表达式的标准。

var projection = Builders<ShapeDocument>.Projection
    .Expression(x => x.fooArray.Where(y => y.plot == "circle"));

var items1 = collection
    .Find(x => x.user == "Jone Doe")
    .Project(projection)
    .ToList();
Run Code Online (Sandbox Code Playgroud)

2)使用聚合管道(您可以使用与上面相同的投影)

var pipeline = collection
    .Aggregate()
    .Match(x => x.user == "Jone Doe")
    .Project(i => new
            {
                x = i.fooArray.Where(x => x.plot == "circle")
            });

var items2 = pipeline.SingleOrDefault();
Run Code Online (Sandbox Code Playgroud)

3) 使用所有数组元素将文档拉回,然后使用 LINQ 进行本地过滤。从好的方面来说,这是少量可读代码,但是,它确实在过滤之前将整个文档带回来。根据您的确切用途,这可能是可以接受的。

var items3 = collection.AsQueryable()
    .SingleOrDefault(x => x.user == "Jone Doe")
    .fooArray.Where(x => x.plot == "circle");
Run Code Online (Sandbox Code Playgroud)

如果LINQ真的不是一个选项,然后有一个例子在这里,告诉您如何可能会在投影转换成不是我们LINQ。完全未经测试,但大致如下:

var filter = new BsonDocument {
 {"input", "$items"},
 {"as", "item" },
 {"cond", new BsonDocument {
     // Fill in the condition values
     { "", new BsonArray { "", xxx } } }
   }
 };

var project = new BsonDocument {
 { "items", new BsonDocument { { "$filter", filter} } }
};

var pipeline = collection.Aggregate().Project(project);
Run Code Online (Sandbox Code Playgroud)