mongodb - Mongo 查询仅返回子文档的一个子集

标签 mongodb mongodb-query aggregation-framework

使用 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/

相关文章:

node.js - 如何使用 VueJs 读取 MongoDB 结果

node.js - RobuSTLy 检索哪个字段在 Mongo 中导致 'duplicate key error'

mongodb - 查找操作后隐藏嵌套文档中的 _id

c# - 如何在 MongoDB C# 中使用地理空间查询?

javascript - Mongodb 通过 $sample 提供唯一值

mongodb - 使用 MongoDB Aggregate 计算两个数组之间的点积

node.js - nodejs + mongodb错误异常: FieldPath 'progress' doesn't start with $

mongodb - 按匹配的子文档排序

MongoDB - 检查文档中的字段是否存在值

node.js - 如何使用聚合索引