使用mongo $ redact在数组中包含嵌入文档和嵌入文档

时间:2015-11-18 06:34:24

标签: mongodb mongodb-query aggregation-framework

对于以下文档,我需要输出

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"
     }
}}]);

2 个答案:

答案 0 :(得分:2)

$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"
            }
    }
}

答案 1 :(得分:2)

使用redact解决上述问题。

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"
        }
    }}
])