MongoDB group by date intervals

前端 未结 2 1438
忘掉有多难
忘掉有多难 2020-12-16 08:15

I have a collection with datetime field and count filed:

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


        
2条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-16 08:39

    There are date aggregation operators available to the aggregation framework of MongoDB. So for example a $dayOfYear operator is used to get that value from the date for use in grouping:

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

    Or you can use a date math approach instead. By applying the epoch date you convert the date object to a number where the math can be applied:

    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" }
        }}
    ])
    

    If what you are after is intervals from a current point in time then what you want is basically the date math approach and working in some conditionals via the $cond operator:

    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
                    ]
                }
            }
        }}
    ])
    

    It's essentially the same approach as the SQL example, where the query conditionally evaluates whether the date value falls within the required range and decides whether or not to add the value to the sum.

    The one addition here is the additional $match stage to restrict the query to only act on those items that would possibly be within the maximum one year range you are asking for. That makes it a bit better than the presented SQL in that an index could be used to filter those values out and you don't need to "brute force" through non matching data in the collection.

    Always a good idea to restrict the input with $match when using an aggregation pipeline.

提交回复
热议问题