【问题标题】:using mongo $redact to include embedded documents and embedded documents within arrays使用 mongo $redact 在数组中包含嵌入文档和嵌入文档
【发布时间】:2015-11-18 06:34:24
【问题描述】:

对于下面的文档,我需要输出

1. image arrays which are hsl:1 
2. dim.hsl embedded document

主文档

{
"_id" : ObjectId("564b17873b91989fcb4e9707"),
"type" : "article",
"image" : [ 
    {
        "name" : "i3",
        "hsl" : 1
    }, 
    {
        "name" : "i1",
        "hsl" : 1
    }, 
    {
        "name" : "i2",
        "hsl" : 0,
        "ai" : 1
    }
],
"dim" : {
    "hsl" : {
        "path" : "blah"
    },
    "ai" : {
        "path" : "blah"
    }
}

我的预期输出如下,它从图像数组和 dim.hsl 嵌入文档中返回 2 个文档

{
    "result" : [ 
        {
            "_id" : ObjectId("564b17873b91989fcb4e9707"),
            "type" : "article",
            "image" : [ 
                {
                    "name" : "i3",
                    "hsl" : 1
                }, 
                {
                    "name" : "i1",
                    "hsl" : 1
                }
            ],
            "dim" : {
                "hsl" : {
                    "path" : "blah"
                }
            }
        }
    ],
    "ok" : 1.0000000000000000
}

下面尝试的代码不返回 dim.hsl 嵌入文档。 该逻辑作为内联包含在代码中。 我不确定 检查节点存在 的 mongodb 表达式。所以我使用了 {$not : {$not : '$hsl'}}。 (希望有更好的方法) 请提出建议。

db.survey.aggregate([
{ $match: { 
     image: { $elemMatch: {hsl: 1}}, //  docs which have image.hsl 
     'dim.hsl': { $exists: true} // docs which have dim.hsl
}},
{ $redact : {
     $cond: {
         if: { $or : [
                 {$eq: ['$hsl',1]}, // for image array document with hsl:1
                 {$eq : ['$type' ,'article']},// for main document node
                 {$not : {$not : '$hsl'}} // for dim -- NOT WORKING
             ]},
         then: "$$DESCEND",
         else: "$$PRUNE"
     }
}}]);

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    $redact 不能这样工作的问题是因为$$DESCEND 及其作用。该表达式基本上意味着正在“在每个级别”检查文档以确定是否满足条件。因此,即使您对存在的"hsl" 键进行了正确测试,它仍将“下降”到"path",该条件不会为真,因此没有有效的内容可以进一步返回上树。

    这里唯一真正的方法是完全“忽略”该密钥,基本上在所有级别提供$$ROOT 检查,而不触及文档的嵌入部分。然后当然你可以稍后用$project 去掉不需要的值:

    db.doc.aggregate([
        { "$match": {
            "image.hsl": 1,
            "dim.hsl": { "$exists": true }
        }},
        { "$redact": {
             "$cond": {
                "if": { 
                    "$or": [
                         { "$eq": ['$hsl',1]},
                         { "$eq": ['$type' ,'article']},
                         { "$ifNull": [ "$$ROOT.dim.hsl", false ] }
                     ]
                },
                "then": "$$DESCEND",
                "else": "$$PRUNE"
             }
        }},
        { "$project": {
            "type": 1,
            "image": 1,
            "dim": {
                "hsl": "$dim.hsl"
            }
        }}
    ])
    

    当然,这并没有真正删除任何东西,因为$ROOT 条件始终为真,这否定了操作的意义。

    所以改为使用$map$setDifference 过滤掉元素:

    db.doc.aggregate([
        { "$match": {
            "image.hsl": 1,
            "dim.hsl": { "$exists": true }
        }},
        { "$project": {
            "type": 1,
            "image": { 
                "$setDifference": [
                    { "$map": {
                        "input": "$image",
                        "as": "el",
                        "in": {
                            "$cond": [
                                { "$eq": [ "$$el.hsl", 1 ] },
                                {
                                    "name": "$$el.name",
                                    "hsl": "$$el.hsl"
                                },
                                false
                            ]
                        }
                    }},
                    [false]
                ]
            },
            "dim": {
                "hsl": "$dim.hsl"
            }
        }}
    ])
    

    这实际上“过滤”了数组内容,方法是使用$map 检查每个元素,然后仅在正向true 条件下返回所需字段,或者返回false 值而不是数组元素。然后,通过与另一个具有 [false] 的数组/集进行比较,从数组中删除所有 false 值,结果只是返回的匹配项。

    其他子键投影保持不变,当然现在所有的删除都只在一个$project阶段完成。

    然后返回你想要的结果:

    {
        "_id" : ObjectId("564b17873b91989fcb4e9707"),
        "type" : "article",
        "image" : [
                {
                        "name" : "i3",
                        "hsl" : 1
                },
                {
                        "name" : "i1",
                        "hsl" : 1
                }
        ],
        "dim" : {
                "hsl" : {
                        "path" : "blah"
                }
        }
    }
    

    【讨论】:

    • 此文档也需要从输出中删除 { "name" : "i2", "hsl" : 0, "ai" : 1 }。 .. 和 { "$ifNull": [ "$$ROOT.dim.hsl", false ] } 如果它是真/假.. 聊天没有任何区别?
    • @faiz 当然。 $$ROOT.dim.hsl 在查询条件下将始终返回 true,因此不会删除任何内容。因此,在解释中添加了另一种方法。
    • 我正在评估您的答案,我明白了。 "errmsg" : "异常:表达式 $setDifference 正好有 2 个参数。传入了 1 个。",
    • @faiz $map 后面少了一个逗号。
    • 是的。想通了失踪的昏迷。可以对您的答案进行内联代码解释。 - 需要读懂你的想法:)。谢谢!
    【解决方案2】:

    使用编辑来解决上述问题。

    db.survey.aggregate([
        { "$match": {
            "image.hsl": 1,
            "dim.hsl": { "$exists": true }
        }},
        { "$redact": {
             "$cond": {
                "if": { 
                    "$or": [
                         { "$eq": ['$hsl',1]},
                         { "$eq": ['$type' ,'article']},
                         { "$ifNull": [ "$hsl",  false] },
                         { "$ifNull": [ "$$CURRENT.path",  false] }
                     ]
                },
                "then": "$$DESCEND",
                "else": "$$PRUNE"
             }
        }},
        { "$project": {
            "type": 1,
            "image": 1,
            "dim": {
                "hsl": "$dim.hsl"
            }
        }}
    ])
    

    【讨论】:

    • 好主意。在这里测试底层path 字段就足够了。它仍然使用比您需要的多一个管道阶段,并且每个阶段都会产生成本。无论如何,我使用的 $map$setDifference 组合在下一个 MongoDB 版本中将被 $filter 取代,这使得它更加灵活,并且不仅限于返回唯一条目的“集合”。
    • 泰。单独测试路径不会返回暗淡。投影管道是额外的开销。我没有遵循独特条目的“组”
    • “集合”(认为数学)意味着“唯一”值,因此任何具有与另一个键值相同的数组条目组合将通过 $setDifference 减少为单个条目,即[1,1,2,3] 变为 [1,2,3] (不一定是有序的)。这就是“集合”的本质。 $filter 是 MongoDB 3.2 及更高版本的答案。 $redact 有效,但如前所述,它不是解决此问题的最佳解决方案。寻找条件使其起作用的道具,但这不是最佳解决方案。
    • 我选择在我的解决方案和 SO 中继续您的答案 - 在投影中使用地图。您在 answer 和 cmets 中的详细解释帮助并提供了更多见解。再次感谢您。
    猜你喜欢
    • 1970-01-01
    • 2015-12-21
    • 2018-07-24
    • 2020-10-31
    • 2018-12-18
    • 1970-01-01
    • 2019-10-10
    相关资源
    最近更新 更多