MongoDB按日期间隔分组

时间:2015-01-03 01:55:38

标签: mongodb mongodb-query aggregation-framework

我有一个日期时间字段和计数字段的集合:

{
    _id: null,
    datetime: new Date(),
    count: 1234
}

我希望在24小时,7天和30天间隔内获得计数的总和。 结果必须如下:

{"sum": 100, "interval": "day"},
{"sum": 700, "interval": "week"},
{"sum": 3000, "interval": "month"}

修改

更抽象:我需要多个条件的组结果(在这种情况下 - 多个时间间隔)

MySQL示例:

SELECT 
    IF (time>CURRENT_TIMESTAMP() - INTERVAL 24 HOUR, 1, 0) last_day,
    IF (time>CURRENT_TIMESTAMP() - INTERVAL 168 HOUR, 1, 0) last_week,
    IF (time>CURRENT_TIMESTAMP() - INTERVAL 720 HOUR, 1, 0) last_month,
    SUM(count) count
FROM table
GROUP BY    last_day,
            last_week,
            last_month

2 个答案:

答案 0 :(得分:17)

MongoDB的聚合框架可以使用date aggregation operators。因此,例如,$dayOfYear运算符用于从分组中使用的日期获取该值:

db.collection.aggregate([
    { "$group": {
        "_id": { "$dayOfYear": "$datetime" },
        "total": { "$sum": "$count" }
    }}
])

或者您可以使用日期数学方法。通过应用纪元日期,您可以将日期对象转换为可应用数学的数字:

db.collection.aggregate([
    { "$group": {
        "_id": { 
            "$subtract": [
                { "$subtract": [ "$datetime", new Date("1970-01-01") ] },
                { "$mod": [
                    { "$subtract": [ "$datetime", new Date("1970-01-01") ] },
                    1000 * 60 * 60 * 24
                ]}
            ]
        },
        "total": { "$sum": "$count" }
    }}
])

如果你所追求的是从当前时间点开始的间隔,那么你想要的基本上是日期数学方法,并通过$cond运算符在某些条件下工作:

db.collection.aggregate([
    { "$match": {
        "datetime": { 
            "$gte": new Date(new Date().valueOf() - ( 1000 * 60 * 60 * 24 * 365 ))
        }
    }},
    { "$group": {
        "_id": null,
        "24hours": { 
            "$sum": {
                "$cond": [
                    { "$gt": [
                        { "$subtract": [ "$datetime", new Date("1970-01-01") ] },
                        new Date().valueOf() - ( 1000 * 60 * 60 * 24 )
                    ]},
                    "$count",
                    0
                ]
            }
        },
        "30days": { 
            "$sum": {
                "$cond": [
                    { "$gt": [
                        { "$subtract": [ "$datetime", new Date("1970-01-01") ] },
                        new Date().valueOf() - ( 1000 * 60 * 60 * 24 * 30 )
                    ]},
                    "$count",
                    0
                ]
            }
        },
        "OneYear": { 
            "$sum": {
                "$cond": [
                    { "$gt": [
                        { "$subtract": [ "$datetime", new Date("1970-01-01") ] },
                        new Date().valueOf() - ( 1000 * 60 * 60 * 24 * 365 )
                    ]},
                    "$count",
                    0
                ]
            }
        }
    }}
])

它与SQL示例的方法基本相同,其中查询有条件地评估日期值是否在所需范围内,并决定是否将值添加到总和中。

此处的一个附加内容是额外的$match阶段,用于限制查询仅针对那些可能在您要求的最大一年范围内的项目。这使得它比所提出的SQL好一点,因为可以使用索引来过滤掉这些值,并且您不需要"暴力"通过集合中的非匹配数据。

使用聚合管道时,最好使用$match限制输入。

答案 1 :(得分:3)

有两种不同的方法可以做到这一点。一种是为每个范围发出单独的count()查询。这非常简单,如果将datetime字段编入索引,则会很快。

第二种方法是使用与SQL示例类似的方法将它们全部合并到一个查询中。为此,您需要使用aggregate()方法,创建$project的管道,为新" last_day"," last_week&#创建0或1值34;和" last_month"字段,然后使用$group运算符来完成总和。