Mongodb C#驱动程序仅返回数组中匹配的子文档

时间:2018-11-09 20:19:18

标签: c# mongodb mongodb-query aggregation-framework mongodb-.net-driver

我有一个这种格式的文件:

{
    _id: ...,
    myArray: [{other: stuff}, {other: stuff}, ...],
    ...
}

我想找到与某些事物匹配的元素,例如_idmyArray子文档中的字段值。

我想返回文档,但是要过滤掉MyArray,其中只显示匹配的子文档。

我试图做一个投影,并包含匹配的元素,如下所示:

_mongoContext.myDocument
    .Find(x => x.id == id & x.myArray.Any(y => myList.Contains(t.other)))
    .Project<myModel>(Builders<myModel>.Projection.Include("myArray.$"))

我认为,这应该只返回myArray中匹配的第一个元素,而不是所有文档,这不是我想要的(我希望所有与查询匹配的子文档都出现在返回的文档中文档)。

无论如何,它甚至都不起作用,我遇到了positional projection does not match the query document错误。也许是因为我没有使用FindOne吗?

在任何情况下,我如何实现所需的内容? (请以粗体显示问题)

1 个答案:

答案 0 :(得分:1)

通常,您需要在Aggregation Framework中使用$filter来过滤嵌套数组。但是,使用MongoDB .NET驱动程序和 IQueryable 界面是实现这一目标的简便方法。

考虑最简单的模型:

public class MyModel
{
    public string _id { get; set; }
    public IEnumerable<MyNestedModel> myArray { get; set; }
}

public class MyNestedModel
{
    public string other { get; set; }
}

及以下数据:

var m = new MyModel()
{
    _id = "1",
    myArray = new List<MyNestedModel>() {
        new MyNestedModel() {  other = "stuff" },
        new MyNestedModel() { other = "stuff" },
        new MyNestedModel() { other = "stuff2" } }
};

Col.InsertOne(m);

您可以简单地在集合上调用.AsQueryable(),然后编写LINQ查询,该查询将由MongoDB驱动程序转换为$filter,请尝试:

var query = from doc in Col.AsQueryable()
            where doc._id == "1"
            select new MyModel()
            {
                _id = doc._id,
                myArray = doc.myArray.Where(x => x.other == "stuff")
            };

var result = query.ToList();

编辑:

或者,您可以将$filter部分作为原始字符串编写,然后使用.Aggregate()方法。使用这种方法,您不必“映射”所有属性,但是缺点是您丢失了类型安全性,因为这只是一个字符串,请尝试:

var addFields = BsonDocument.Parse("{ \"$addFields\": { myArray: { $filter: { input: \"$myArray\", as: \"m\", cond: { $eq: [ \"$$m.other\", \"stuff\" ] } }  } } }");

var query = Col.Aggregate()
               .Match(x => x._id == "1")
               .AppendStage<MyModel>(addFields);

$addFields用于覆盖现有字段。