使用 Mongo 文档中的示例:
{ _id: 1, results: [ { product: "abc", score: 10 }, { product: "xyz", score: 5 } ] }
{ _id: 2, results: [ { product: "abc", score: 8 }, { product: "xyz", score: 7 } ] }
{ _id: 3, results: [ { product: "abc", score: 7 }, { product: "xyz", score: 8 } ] }
db.survey.find(
{ id: 12345, results: { $elemMatch: { product: "xyz", score: { $gte: 6 } } } }
)
如何返回调查 12345(无论是否有调查)但仅返回得分大于 6 的调查?换句话说,我不希望文档从基于子文档的结果中取消资格,我想要文档,但只需要子文档的子集。
最佳答案
您要求的与其说是“查询”,不如说是对每个文档中的数组内容进行过滤。
您使用 .aggregate()
执行此操作和 $project
:
db.survey.aggregate([
{ "$project": {
"results": {
"$setDifference": [
{ "$map": {
"input": "$results",
"as": "el",
"in": {
"$cond": [
{ "$and": [
{ "$eq": [ "$$el.product", "xyz" ] },
{ "$gte": [ "$$el.score", 6 ] }
]}
]
}
}},
[false]
]
}
}}
])
因此,不是将结果“限制”到具有与条件匹配的数组成员的文档,而是“过滤”出与条件不匹配的数组成员,但如果需要则返回具有空数组的文档是。
目前最快的方法是使用 $map
检查所有元素和$setDifference
过滤掉从该检查返回的所有 false
值。可能的缺点是“集合”必须包含唯一元素,所以只要元素本身是唯一的,这就没问题。
future 版本将有一个$filter
方法,在结构上类似于$map
,但直接去除不匹配的结果,其中as $map
只是返回它们(通过 $cond
和匹配元素或 false
)然后更适合。
否则,如果不是唯一的或 MongoDB 服务器版本低于 2.6,您将以非高效的方式使用 $unwind
执行此操作:
db.survey.aggregate([
{ "$unwind": "$results" },
{ "$group": {
"_id": "$_id",
"results": { "$push": "$results" },
"matched": {
"$sum": {
"$cond": [
{ "$and": [
{ "$eq": [ "$results.product", "xyz" ] },
{ "$gte": [ "$results.score", 6 ] }
]},
1,
0
]
}
}
}},
{ "$unwind": "$results" },
{ "$match": {
"$or": [
{
"results.product": "xyz",
"results.score": { "$gte": 6 }
},
{ "matched": 0 }
}},
{ "$group": {
"_id": "$_id",
"results": { "$push": "$results" },
"matched": { "$first": "$matched" }
}},
{ "$project": {
"results": {
"$cond": [
{ "$ne": [ "$matched", 0 ] },
"$results",
[]
]
}
}}
])
这在设计和性能上都非常糟糕。因此,您最好改为在客户端代码中按文档进行过滤。
关于mongodb - Mongo 查询仅返回子文档的一个子集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33717779/