计算与给定条件匹配的子文档

ilP*_*tiz 3 mongodb mongodb-query aggregation-framework

我的Mongo集合包含以下形式的文档:

{
    ...
    "notifications": [
        {
            "enabled": true,
            "groups": [ "NG1", "NG3" ]
        },
        {
            "enabled": false,
            "groups": []
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

其中enabled是布尔值,groups是一个字符串列表.我需要执行的查询来确定有多少项notificationsenabled = true和包含给定的字符串groups(如NG3).

以前,没有enabled后来作为要求引入的属性,我的查询很简单

db.collection.find({ "notifications.groups": "NG3" })
Run Code Online (Sandbox Code Playgroud)

我尝试了一些与$and运营商的组合,但没有运气,所以任何建议都是受欢迎的.提前致谢!

chr*_*dam 7

建议运行聚合框架管道,该管道在管道步骤中使用$filter$size数组运算符的组合$project.

所述$filter操作者将在匹配指定条件是阵列的一个子集返回与元素的数组.在$size将简单地返回已过滤的数组中的元素数目.

因此,完全放置这个,您可以运行此管道,以便您可以识别通知中有多少条目enabled = true并包含组中的给定字符串(例如"NG3"):

var pipeline = [
    { "$match": { "notifications.enabled": true, "notifications.groups": "NG3" } },
    {
        "$project": {
            "numberOfEntries": {
                "$size": {
                    "$filter": {
                        "input": "$notifications",
                        "as": "items",
                        "cond": { 
                            "$and": [
                                { "$eq": [ "$$items.enabled", true ] },
                                { "$setIsSubset": [ [ "NG3" ], "$$items.groups" ] }
                            ] 
                        }
                    }
                }
            }
        }
    }
];

db.collection.aggregate(pipeline);
Run Code Online (Sandbox Code Playgroud)

以上适用于MongoDB版本3.2.X和更新版本.但是,对于覆盖MongoDB的版本中的一个溶液2.6.X up to and including 3.0.X,其它阵列运营商等$map,$setDifference将是很好的替代品运营商用于滤波阵列.

考虑使用$map运算符来过滤数组,使用与$ cond中相同的逻辑作为映射表达式.然后,$setDifference运算符返回一个集合,其中的元素出现在第一个集合中但不出现在第二个集合中; 即执行第二组相对于第一组的相对称赞.在这种情况下,它将通过enabledgroups属性返回包含与父文档无关的元素的最终通知数组.

var pipeline = [   
    { "$match": { "notifications.enabled": true, "notifications.groups": "NG3" } },
    {
        "$project": {
            "numberOfEntries": {
                "$size": {
                    "$setDifference": [
                        {
                            "$map": {
                                "input": "$notifications",
                                "as": "items",
                                "in": {
                                    "$cond": [
                                        { "$and": [
                                            { "$eq": [ "$$items.enabled", true ] },
                                            { "$setIsSubset": [ [ "NG3" ], "$$items.groups" ] }
                                        ] },
                                        "$$items",
                                        false
                                    ]
                                }
                            }
                        },
                        [false]
                    ]
                }
            }
        }
    }
];

db.collection.aggregate(pipeline);
Run Code Online (Sandbox Code Playgroud)

对于不具备上述运营商较旧版本的MongoDB,可以考虑使用$match,$unwind$group运营商实现同样的目标:

var pipeline = [
    { "$match": { "notifications.enabled": true, "notifications.groups": "NG3" } },
    { "$unwind": "$notifications" },
    { "$match": { "notifications.enabled": true, "notifications.groups": "NG3" } },
    {
        "$group": {
            "_id": "$_id",
            "numberOfEntries": { "$sum": 1 }
        }
    }
];
db.collection.aggregate(pipeline);
Run Code Online (Sandbox Code Playgroud)

  • 这比我预期的建议更多!! 我从你的答案中学到了很多,所以非常感谢chridam和Ali Dehghani !! (3认同)