【问题标题】:Mongodb count all array elements in all objects matching by criteriaMongodb计算所有按条件匹配的对象中的所有数组元素
【发布时间】:2015-08-31 07:20:36
【问题描述】:

我有一个这样的对象的活动日志集合:

{
    "_id" : ObjectId("55e3fd1d7cb5ac9a458b4567"),
    "object_id" : "1",
    "activity" : [ 
        {
            "action" : "test_action",
            "time" : ISODate("2015-08-31T00:00:00.000Z")
        },
        {
            "action" : "test_action",
            "time" : ISODate("2015-08-31T00:00:22.000Z")
        }
    ]
}

{
    "_id" : ObjectId("55e3fd127cb5ac77478b4567"),
    "object_id" : "2",
    "activity" : [ 
        {
            "action" : "test_action",
            "time" : ISODate("2015-08-31T00:00:00.000Z")
        }
    ]
}

{
    "_id" : ObjectId("55e3fd0f7cb5ac9f458b4567"),
    "object_id" : "1",
    "activity" : [ 
        {
            "action" : "test_action",
            "time" : ISODate("2015-08-30T00:00:00.000Z")
        }
    ]
}

如果我做以下查询:

db.objects.find({
    "createddate": {$gte : ISODate("2015-08-30T00:00:00.000Z")},
    "activity.action" : "test_action"}
    }).count()

它返回包含“test_action”的文档计数(本集中 3 个),但我需要获取所有 test_actions 的计数(本集中 4 个)。我该怎么做?

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    执行此操作的最“高效”方法是完全跳过$unwind 并简单地计算$group。本质上“过滤”数组将结果的$size 获取到$sum

    db.objects.aggregate([
        { "$match": {
            "createddate": {
                "$gte": ISODate("2015-08-30T00:00:00.000Z")
            },
            "activity.action": "test_action"
        }},
        { "$group": {
            "_id": null,
            "count": {
                "$sum": {
                    "$size": {
                        "$setDifference": [
                            { "$map": {
                                "input": "$activity",
                                "as": "el",
                                "in": {
                                    "$cond": [ 
                                        { "$eq": [ "$$el.action", "test_action" ] },
                                        "$$el",
                                        false
                                    ]
                                }               
                            }},
                            [false]
                        ]
                    }
                }
            }
        }}
    ])
    

    MongoDB 的未来版本将拥有$filter,这让这变得更加简单:

    db.objects.aggregate([
        { "$match": {
            "createddate": {
                "$gte": ISODate("2015-08-30T00:00:00.000Z")
            },
            "activity.action": "test_action"
        }},
        { "$group": {
            "_id": null,
            "count": {
                "$sum": {
                    "$size": {
                        "$filter": {
                            "input": "$activity",
                            "as": "el",
                            "cond": {
                                "$eq": [ "$$el.action", "test_action" ]
                            }
                        }
                    }
                }
            }
        }}
    ])
    

    使用$unwind 会导致文档去规范化并有效地为每个数组条目创建一个副本。在可能的情况下,您应该避免这种情况,因为这通常会带来极高的成本。相比之下,过滤和计算每个文档的数组条目要快得多。与许多阶段相比,简单的$match$group 管道也是如此。

    【讨论】:

    • 非常感谢。在大型数据集上必须避免“$unwind”。查询就像一个魅力。我的知识现在很基础,我实际上还不知道它是如何工作的 :) 但找出这一点将是我今天的功课)
    【解决方案2】:

    您可以使用聚合来做到这一点:

    db.objects.aggregate([
        {$match: {"createddate": {$gte : ISODate("2015-08-30T00:00:00.000Z")}, {"activity.action" : "test_action"}}},
        {$unwind: "$activity"},
        {$match: {"activity.action" : "test_action"}}},
        {$group: {_id: null, count: {$sum: 1}}}
    ])
    

    这将产生如下结果:

    {
        count: 4
    }
    

    【讨论】:

    • 谢谢,它可以工作,但它似乎不使用索引并且在 600k 文档数据集上工作得非常慢。我有_idcreateddateactivity.action 的索引。我还应该创建哪些索引?
    • 聚合确实使用了 $match 阶段的索引(如果在开头指定),但正如布莱克斯七人所说,展开阶段会导致大量开销。
    • 我已经添加了一个编辑,这应该让它运行得更快一点
    • 是的,在展开之前过滤它是个好主意。工作得更快。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2015-02-11
    • 2020-06-19
    • 2019-02-18
    相关资源
    最近更新 更多