【问题标题】:How to filter array in a mongodb query如何在 mongodb 查询中过滤数组
【发布时间】:2015-07-24 06:42:13
【问题描述】:

在 mongodb 中,我有一个包含单个文档的集合,如下所示:

{ 
    "_id" : ObjectId("5552b7fd9e8c7572e36e39df"), 
    "StackSummaries" : [
        {
            "StackId" : "arn:aws:cloudformation:ap-southeast-2:406119630047:stack/XXXX-30fb22a-285-439ee279-c7c8d36/4ebd8770-f8f4-11e4-bf36-503f2370240f", 
            "TemplateDescription" : "XXXX", 
            "StackStatusReason" : "", 
            "CreationTime" : "2015-05-12T22:14:50.535Z", 
            "StackName" : "XXXX", 
            "StackStatus" : "CREATE_COMPLETE"
        }, 
        {
            "TemplateDescription" : "XXXX", 
            "StackStatusReason" : "", 
            "CreationTime" : "2015-05-11T04:02:05.543Z", 
            "StackName" : "XXXX", 
            "StackStatus" : "DELETE_COMPLETE", 
            "StackId" : "arn:aws:cloudformation:ap-southeast-2:406119630047:stack/XXXXX/7c8d04e0-f792-11e4-bb12-506726f15f9a"
        },
        { ... },
        { many others }
    ]
}

aws cli命令的导入结果aws cloudformation list-stacks

我正在尝试查找具有StackStatusCREATE_COMPLETEUPDATE_COMPLETEStackSummaries 数组的项目。经过大量试验和阅读其他 SO 帖子后,我得出以下结论:

db.cf_list_stacks.aggregate( {$match: {"StackSummaries.StackStatus": "CREATE_COMPLETE"}})

但是这仍然会返回整个文档(我什至不担心 UPDATE_COMPLETE)。

我来自 SQL 背景,正在努力处理这样的简单查询。关于如何获取我正在寻找的信息的任何想法?

我看过的帖子:

更新

关于我在理解这个主题时学到的东西的注释:

  • aggregate() 只是一个 管道(就像 Unix shell 管道),其中每个 $ 运算符只是另一个步骤。就像 shell 管道一样,它们看起来很复杂,但您只需逐步构建它们,直到获得您想要的结果
  • Mongo 有一个很棒的网络研讨会:Exploring the Aggregation Framework
  • RoboMongo 是处理 Mongo 数据和查询的好工具 (GPL3)

【问题讨论】:

  • 你只有哪些子文件符合给定的条件?您的情况的预期输出是什么?
  • 嗨@Michael,集合中只有一个文档,所以我想“转换”文档,以便 StackSummaries 数组只包含 StackStatus 为 CREATE_COMPLETE 或 UPDATE_COMPLETE 的元素。可能其他答案会对我有所帮助,我刚刚醒来还没有完全理解它们。

标签: mongodb


【解决方案1】:

如果您只想要 StackSummaries 数组中的对象,您应该使用 $unwind 子句扩展数组,过滤您想要的文档,然后只投影您真正想要的文档部分。

查询看起来像这样:

db.cf_list_stacks.aggregate([
    { '$unwind' : '$StackSummaries' },
    { '$match' : { 'StackSummaries.StackStatus' : 'CREATE_COMPLETE' } },
    { '$project' : { 
         'TemplateDescription' : '$StackSummaries.TemplateDescription',
         'StackStatusReason' : '$StackSummaries.StackStatusReason',
         ...
    } }
])

有用的链接:

【讨论】:

  • 感谢@SanSS 和 Chridam 的出色回答,我非常感谢。我都投了赞成票,我需要一段时间才能理解答案,然后才能对“正确”的答案进行投票(尽管它们可能都是正确的)。
【解决方案2】:

在 MongoDB 3.4 和更高版本中,您可以利用 $addFields$filter 运算符和 aggregation framework 来获得所需的结果。

考虑运行以下管道:

db.cf_list_stacks.aggregate([
    {
        "$addFields": {
            "StackSummaries": {
                "$filter": {
                    "input": "$StackSummaries",
                    "as": "el":
                    "cond": {
                        "$in": [ 
                            "$$el.StackStatus", 
                            ["CREATE_COMPLETE", "UPDATE_COMPLETE"] 
                        ] 
                    }
                }
            }
        }
    }
]);

对于 MongoDB 3.2

db.cf_list_stacks.aggregate([
    {
        "$project": {
            "StackSummaries": {
                "$filter": {
                    "input": "$StackSummaries",
                    "as": "el":
                    "cond": {
                        "$or": [
                            { "$eq": ["$$el.StackStatus", "CREATE_COMPLETE"] },
                            { "$eq": ["$$el.StackStatus", "UPDATE_COMPLETE"] }
                        ]
                    }
                }
            }
        }
    }
]);

对于 MongoDB 3.0 及以下版本

db.cf_list_stacks.aggregate([
    { "$unwind": "$StackSummaries" },
    {
        "$match": {            
            "StackSummaries.StackStatus": {
                "$in": ["CREATE_COMPLETE", "UPDATE_COMPLETE"]
            }
        }        
    },
    {
        "$group": {
            "_id": "$_id",
            "StackSummaries": {
                "$addToSet": "$StackSummaries"
            }
        }
    }
])

上述管道具有$unwind 运算符,它从输入文档中解构StackSummaries 数组字段以输出每个元素的文档。每个输出文档都将数组替换为一个元素值。

$unwind 之后需要进一步过滤以仅获取通过给定条件的文档,因此接下来是第二个$match 运算符管道阶段。

为了在执行$unwind 位后获得原始数组字段,您需要使用$group 运算符对文档进行分组,然后在组内您可以使用$addToSet 数组运算符然后推送数组中的元素。


根据您尝试查找具有StackStatusCREATE_COMPLETE OR UPDATE_COMPLETEStackSummaries 数组项的条件,您可以使用$elemMatch projection,但是这不适用于 $in 运算符,因为此时需要使用 StackStatusCREATE_COMPLETE OR UPDATE_COMPLETE 获取文档。有一个 JIRA 问题:

db.cf_list_stacks.find(
    {
        "StackSummaries.StackStatus": {
            "$in": ["CREATE_COMPLETE", "UPDATE_COMPLETE"]
        }
    }, 
    {
        "StackSummaries": {
            "$elemMatch": {
                "StackStatus": {
                    "$in": ["CREATE_COMPLETE", "UPDATE_COMPLETE"]
                }
            }            
        }    
    })

这只会为您提供StackStatus 具有"CREATE_COMPLETE" 值的文档。

【讨论】:

  • 感谢 SanSS 和 @Chridam 的出色回答,我非常感谢。我都投了赞成票,我需要一段时间才能理解答案,然后才能对“正确”的答案进行投票(尽管它们可能都是正确的)。
  • @chridam 我认为您的聚合管道中有一个额外的$match 操作。她明确表示该集合只有一个文档,因此您的聚合的第一个 $match 不会保存任何实际工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-19
  • 2021-07-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-14
相关资源
最近更新 更多