Mongodb C# 驱动程序 return 仅匹配数组中的子文档
Mongodb C# driver return only matching sub documents in array
我有一个这种格式的文档:
{
_id: ...,
myArray: [{other: stuff}, {other: stuff}, ...],
...
}
我想从 myArray
.
中的子文档中找到匹配某些东西的元素,例如 _id
或字段值
我想要 return 文档,但是过滤后 MyArray
只有匹配的子文档存在。
我试着做一个投影并包含这样的匹配元素:
_mongoContext.myDocument
.Find(x => x.id == id & x.myArray.Any(y => myList.Contains(t.other)))
.Project<myModel>(Builders<myModel>.Projection.Include("myArray.$"))
我认为这应该只是 return 中匹配的第一个元素 myArray
而不是所有文档,这不是我想要的(我想要匹配查询的所有子文档出现在 returned 文档中)。
而且无论如何它都没有用,我收到 positional projection does not match the query document
错误。可能是因为我没有使用 FindOne
?
无论如何,我怎样才能实现我正在寻找的东西? (请参阅粗体问题)
通常您需要在聚合框架中使用 $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()
方法。使用这种方法,您不必 "map" 所有属性,但缺点是您正在失去类型安全性,因为这只是一个字符串,请尝试:
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
用于覆盖现有字段。
我有一个这种格式的文档:
{
_id: ...,
myArray: [{other: stuff}, {other: stuff}, ...],
...
}
我想从 myArray
.
_id
或字段值
我想要 return 文档,但是过滤后 MyArray
只有匹配的子文档存在。
我试着做一个投影并包含这样的匹配元素:
_mongoContext.myDocument
.Find(x => x.id == id & x.myArray.Any(y => myList.Contains(t.other)))
.Project<myModel>(Builders<myModel>.Projection.Include("myArray.$"))
我认为这应该只是 return 中匹配的第一个元素 myArray
而不是所有文档,这不是我想要的(我想要匹配查询的所有子文档出现在 returned 文档中)。
而且无论如何它都没有用,我收到 positional projection does not match the query document
错误。可能是因为我没有使用 FindOne
?
无论如何,我怎样才能实现我正在寻找的东西? (请参阅粗体问题)
通常您需要在聚合框架中使用 $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()
方法。使用这种方法,您不必 "map" 所有属性,但缺点是您正在失去类型安全性,因为这只是一个字符串,请尝试:
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
用于覆盖现有字段。