使用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的调查?换句话说,我不希望文档被取消基于子文档的结果,我想要文档但只是子文档的子集。
答案 0 :(得分:3)
您要求的不是“查询”,而是基本上只是对每个文档中数组内容的过滤。
您使用.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
值。可能的缺点是“set”必须包含唯一元素,所以只要元素本身是唯一的就可以了。
将来的版本将采用$filter
方法,类似于$map
的结构,但直接删除不匹配的结果,而$map
只返回它们(通过{{1并且匹配元素或$cond
)然后更适合。
否则,如果不是唯一的或者MongoDB服务器版本小于2.6,则使用false
以非高效的方式执行此操作:
$unwind
在设计和性能方面都非常糟糕。因此,您可能最好在客户端代码中对每个文档进行过滤。
答案 1 :(得分:2)
您可以在mongoDB 3.2中使用$ filter
db.survey.aggregate([{
$match: {
{ id: 12345}
}
}, {
$project: {
results: {
$filter: {
input: "$results",
as: "results",
cond:{$gt: ['$$results.score', 6]}
}
}
}
}]);
它将返回分数大于6的所有子文档。如果您只想返回第一个匹配的文档,那么您可以使用' $'操作
答案 2 :(得分:1)
您可以这样使用$ redact:
db.survey.aggregate( [
{ $match : { _id : 12345 }},
{ $redact: {
$cond: {
if: {
$or: [
{ $eq: [ "$_id", 12345 ] },
{ $and: [
{ $eq: [ "$product", "xyz" ] },
{ $gte: [ "$score", 6 ] }
]}
]
},
then: "$$DESCEND",
else: "$$PRUNE"
}
}
}
] );

首先匹配_id:12345然后它将" $$ PRUNE"所有没有产品的子文档"产品":" xyz"并且没有得分大于或等于6.我添加条件($ cond){$ eq:[" $ _ id",12345]}以便它不会修剪整个文档到达子文档之前。