MongoDB中elemmatch多个数组元素

15

我有一个类似于 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个回答

23

使用基本的.find()查询无法返回与您条件匹配的数组的多个元素。要匹配多个元素,需要使用.aggregate()方法。

主要区别在于“查询”正是其旨在做的事情,并匹配符合条件的“文档”。您可以尝试在投影参数中使用位置占位符$运算符,但规则是它只会匹配符合查询条件的“第一个”数组元素。

为了“过滤”多个数组元素,请按照以下步骤进行:

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

在MongoDB现代版本(版本2.6或更高)中,可以使用$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"
        ]
    }}
])

这可能是您最有效的选项,但它是递归的,因此请先考虑您的文档结构,因为同名字段不能与任何其他条件在任何级别上存在。

也许更安全,但仅当数组中的结果“真正独特”时才有用的技术是使用 $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。当仅访问数组的单个键时,推荐使用“点符号”。只有在文档内有“多个”键需要与查询条件匹配的数组“元素”时,才需要使用$elemMatch


这些答案中的 filtermetric 应该改为 FilterMetric,以匹配样例文档中该字段的大小写。 - JohnnyHK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接