Mongodb - 在一个查询中查询今天的总计,周总计和月份总计

时间:2015-10-29 21:40:10

标签: node.js mongodb mongodb-query aggregation-framework

我的数据库中的对象看起来像这样:

{
    "_id": ObjectId("563f8c320ef987c122aeeb4a"),
    "num": 1515,
    "createdAt": ISODate("2015-10-29T21:14:26.477Z"),
}

我希望编写一个聚合,按特定ID对所有内容进行分组,并汇总今天,本周和本月的总计,并在一个查询中执行。我为此任务编写了三个单独的查询,但我想知道我是否可以提高效率并在一次查询中完成。

修改

有人提到mapReduce作为解决方案。这似乎是一个很有前途的解决方案,但我无法从简单的查询中获得任何回报。以下是我的尝试:

    var o = {};
    o.map = function () { emit( this.num, this.createdAt ) }
    o.reduce = function (k, vals) { return vals }
    o.query = {
        _id: req.user._id
    }
    Submission.mapReduce(o, function (err, results) {
        console.log(results)
    })

控制台记录一个空数组。我也尝试将_id转换为mongoose对象id,但它仍然返回一个空数组。

1 个答案:

答案 0 :(得分:3)

这更像是一个你期望输出看起来像什么的问题,因为任何聚合结果基本上都需要在最低级别进行分组,然后逐步分组在更高的" grain"直到达到最大水平("月")。这种意味着按"月"分组的数据。最终,除非你打破它。否则。

实质上,逐步$group

db.collection.aggregate([
    // First total per day. Rounding dates with math here
    { "$group": {
        "_id": {
            "$add": [
                { "$subtract": [
                    { "$subtract": [ "$createdAt", new Date(0) ] },
                    { "$mod": [
                        { "$subtract": [ "$createdAt", new Date(0) ] },
                        1000 * 60 * 60 * 24
                    ]}                        
                ]},
                new Date(0)
            ]
        },
        "week": { "$first": { "$week": "$createdAt" } },
        "month": { "$first": { "$month": "$createdAt" } },
        "total": { "$sum": "$num" }
    }},

    // Then group by week
    { "$group": {
        "_id": "$week",
        "month": { "$first": "$month" },
        "days": {
            "$push": {
                "day": "$_id",
                "total": "$total"
            }
        },
        "total": { "$sum": "$total" }
    }},

    // Then group by month
    { "$group": {
        "_id": "$month",
        "weeks": {
            "$push": {
                "week": "$_id",
                "total": "$total",
                "days": "$days"
            }
        },
        "total": { "$sum": "$total" }
    }}
])

因此,每天在总和之后的每个级别逐渐被推入到数组内容中,以便它可以向上"向上"然后,价值和总数也会在该水平上相加。

如果你想要一个更平坦的输出,每天有一条记录包含它的每周和每月总数以及总日数,那么只需在管道的末尾添加两个$unwind语句:

{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }

可选$project"点缀"如果你必须这样做,就会变得更加平坦和可读。

如果你正在跨越"岁月"有了这个,那么至少从"周"在分组键中包括这样的操作。因此,您不可能组合来自不同年份的数据,并将它们分开。

我自己一般倾向于在舍入日期时使用"date math"方法,因为它返回Date个对象,但是在其他级别使用{" day" ,您可以改为使用date aggregation operators代替。

不需要mapReduce,因为这是相当直观的,并且一个月内的天数有限,这意味着在内容中嵌套数组时BSON限制,而聚合不会被破坏。