如何通过$ setDifference找到包含特定键的所有子文档?

时间:2016-02-24 07:29:07

标签: mongodb mongodb-query aggregation-framework

对于此问题Find documents with arrays that contain a document with a particular field的评论,$setDifference$map应解决此问题。以下是这个问题的详细信息。

  

我想只查找数组'docs.foo'中存在所有 docs的文档。鉴于以下两个文件

{  
    _id : 1,
    docs : [
        { bar : 2},
        { foo : 3, bar : 3}
    ]
},
{  
    _id : 2,
    docs : [
        { foo : 2, bar : 2},
        { foo : 3, bar : 3}
    ]
} 

这是我的代码

db.collection.aggregate([ 
    {$project: {
        docs: {$setDifference: 
                [{$map:
                    {input: '$docs', 
                    as: 'item', 
                    in: {$cond: [{$ne: [
                                    {$ifNull: ['$$item.foo', null]}, 
                                    null]}, 
                               '$$item', 
                               null]}}
                }, 
                [null]
                ]
            }
    }}
])

然而,结果是

{  
    _id : 1,
    docs : [
        { foo : 3, bar : 3}
    ]
},
{  
    _id : 2,
    docs : [
        { foo : 2, bar : 2},
        { foo : 3, bar : 3}
    ]
} 

仅从{bar: 2}文档中删除了_id: 1。我希望输出是

{  
    _id : 1,
    docs : [ ]
},
{  
    _id : 2,
    docs : [
        { foo : 2, bar : 2},
        { foo : 3, bar : 3}
    ]
} 

是否可以通过$setDifference

执行此操作

1 个答案:

答案 0 :(得分:3)

这里你要求的是与引用的问题有点不同,因为如果其中包含的所有文档与一组条件不匹配,你故意想要使数组“为空”。

在这种情况下,此处更好的运算符为$allElementsTrue,您可以将其应用于$map,如下所示:

db.collection.aggregate([
    { "$project": {
        "docs": {
            "$cond": {
                "if": {
                    "$allElementsTrue": {
                        "$map": { 
                            "input": "$docs",
                            "as": "el",
                            "in": { "$ifNull": [ "$$el.foo", false ] }
                        }
                    }
                },
                "then": "$docs",
                "else": { "$literal": [] }
            }
        }
    }}
])

因此逻辑基本上通过$map测试每个数组元素而不是“过滤”数组内容,而是返回true/false值的数组。如果“所有元素确实都是true,那么这将产生单个true/false值作为响应。

然后由三元$cond考虑​​这一点,它说true(所有条件都匹配)然后只是呈现已经形成的数组。但是当这是false(并非所有匹配的)而是返回一个空数组时。

非常确定$literal参数并不真正需要"else",并且只能以[]的形式写入。但我只是在确定何时只是在这里键入逻辑而不是测试。

但是这个逻辑会返回“空”结果,因为“all”元素不匹配数组中所有子文档都包含"foo"键的必要条件。