MongoDB elemmatch数组中的多个元素

时间:2015-03-13 07:10:14

标签: mongodb mongodb-query aggregation-framework

我有一个像

这样的mongodb文档
    {
        "_id" : ObjectId("54e66b2da7b5f3a92e09dc6c"),
        "SomeMetric" : [ 
            {
                //some object
            }
            {
                //some object
            } 
         ],
        "FilterMetric" : [ 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s1"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s2"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s1"
            }, 
            {
                "min" : "0.00",
                "max" : "16.83",
                "avg" : "0.00",
                "class" : "s2"
            } 
        ]
    }

通常它包含许多这样的嵌套数组。我想单独设计一个指标,只有具有我搜索条件的数组。 我有查询

db.sample.find(
{"filtermetric.class" : "s2"},{"filtermetric" : { $elemMatch : {class: "s2"}}}
)

这只给了我数组中的第一个对象。不返回带有类:s2的第二个对象。

如果我尝试

    db.sample.find(
   {"filtermetric" : { $elemMatch : {class: "s2"}}}
    )

它给了我数组中的所有4个对象。

在这种情况下,如何获得符合条件的所有对象?

1 个答案:

答案 0 :(得分:17)

您不能以任何形式的基本.find()查询返回符合条件的数组的多个元素。要匹配多个元素,您需要使用.aggregate()方法。

这里的主要区别在于"查询"完全按照它的意图去做并匹配"文件"符合你的条件。您可以尝试在投影参数中使用positional $运算符,但规则是它只匹配"第一个"与查询条件匹配的数组元素。

为了过滤"对于多个数组元素,请按以下步骤操作:

db.sample.aggregate([
    // Filter possible documents
    { "$match": { "filtermetric.class": "s2" } },

    // Unwind the array to denormalize
    { "$unwind": "$filtermetric" },

    // Match specific array elements
    { "$match": { "filtermetric.class": "s2" } },

    // Group back to array form
    { "$group": {
        "_id": "$_id",
        "filtermetric": { "$push": "$filtermetric" }
    }}
])

在版本为2.6或更高版本的MongoDB的现代版本中,您可以使用$redact执行此操作:

db.sample.aggregate([
    // Filter possible documents
    { "$match": { "filtermetric.class": "s2" } },

    // Redact the entries that do not match
    { "$redact": {
        "$cond": [
            { "$eq": [ { "$ifNull": [ "$class", "s2" ] }, "s2" ] },
            "$$DESCEND",
            "$$PRUNE"
        ]
    }}
])

这可能是你最有效的选择,但它是递归的,所以首先考虑你的文档结构,因为任何级别的任何其他条件都不能存在相同的命名字段。

可能更安全,但只有在数组中的结果是真正独特的时候才有用#34;是$map$setDifference的这种技术:

db.sample.aggregate([
    { "$project": {
        "filtermetric": { "$setDifference": [
            { "$map": [
                "input": "$filtermetric",
                "as": "el",
                "in": {"$cond": [
                    { "$eq": [ "$$el.class", "s2" ] },
                    "$$el",
                    false
                ]}
            ]},
            [false]
        ]}
    }}
])

另请注意,在$group$project操作管道阶段,需要指定您要在该阶段的结果文档中返回的所有字段。

最后需要注意的是,当您只查询数组中单个键的值时,不需要$elemMatch。仅访问阵列的单个键时,首选"Dot notation"并建议使用{{3}}。仅当"多个"时才需要$elemMatch数组中文件中的键"元素"需要匹配查询条件。