查找包含具有特定字段的文档的数组的文档

时间:2016-02-17 08:06:02

标签: mongodb mongodb-query aggregation-framework

我想只查找数组中存在所有'docs.foo'的文档。 在我的示例中,我应该只获得_id: 2结果:

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

我想到了类似的东西:

db.collection.find({'docs.foo': {$nin: [$exists: false]}})

但无法使其发挥作用。

1 个答案:

答案 0 :(得分:3)

使用$where运算符。

db.collection.find(function() { 
    return this.docs.length === this.docs.filter(function(doc) {
        return typeof(doc.foo) !== "undefined" && doc.foo !== null ;}).length 
})

另一种方法是运行两个查询:一个查询使用distinct()方法检索所有不符合条件的文档的_id

var unwantedIds = db.collection.distinct( "_id", { "docs": { "$elemMatch": { "foo": { "$exists": false } } } } );

然后使用$nin运算符返回符合条件的所有文档。

db.collection.find({ "_id": { "$nin": unwantedIds } } )

您也可以使用.aggregate()方法但这仅适用于3.2或更高版本,因为您需要使用$filter

管道中的第一个阶段是$match阶段,您可以过滤掉那些" foo"场缺席。这减少了将在管道中处理的文档总数。下一个和最后一个阶段是$redact阶段。在此阶段,您需要使用$size运算符来返回" docs"的大小。字段和子文档数组的大小,其中" foo"存在并返回两个值相等的所有文档。

db.collection.aggregate([
    { "$match": { "docs.foo": { "$exists": true } } }, 
    { "$redact": { 
        "$cond": [ 
            { "$eq": [ 
                { "$size": "$docs" }, 
                { "$size":  { 
                    "$filter": { 
                        "input": "$docs", 
                        "as": "doc", 
                        "cond": { 
                            "$ne": [ 
                                { "$ifNull": [ "$$doc.foo", null ] },
                                null 
                            ] 
                        } 
                    }
                }}
            ]}, 
            "$$KEEP", 
            "$$PRUNE"
        ]
    }}
])