Mongodb汇总多个日期范围的$ group

时间:2015-11-11 18:21:45

标签: mongodb mongodb-query aggregation-framework

在我的聚合中,流中的每个文档都会有一个日期。

我需要在日期范围内总结一些值..

{
    value: 3,
    date: [SoME TIME STAMP]
},
{
    value: 4,
    date: [SoME TIME STAMP]
},
{
    value: 1,
    date: [SoME TIME STAMP]
},
{
    value: -6,
    date: [SoME TIME STAMP]
}

我希望能够根据日期范围对这些文档进行分组。 IE:1-7天前,8-15天前。和15-30天前。

db.Collection.aggregate([
{$match: {some matching}},
{$group: {What should i do here??}}
])

我当然可以做3种不同的聚合,在日期上有3种不同的$ match。

是否可以执行所有$ group并在一次运行中对“value”字段求和?

2 个答案:

答案 0 :(得分:24)

您需要根据当前日期在范围之间的位置有条件地确定分组键。这基本上是通过$cond使用嵌套条件和$lt的逻辑变体实现的:

// work out dates somehow
var today = new Date(),
    oneDay = ( 1000 * 60 * 60 * 24 ),
    thirtyDays = new Date( today.valueOf() - ( 30 * oneDay ) ),
    fifteenDays = new Date( today.valueOf() - ( 15 * oneDay ) ),
    sevenDays = new Date( today.valueOf() - ( 7 * oneDay ) );

db.collection.aggregate([
    { "$match": {
        "date": { "$gte": thirtyDays }
    }},
    { "$group": {
        "_id": {
            "$cond": [
                { "$lt": [ "$date", fifteenDays ] },
                "16-30",
                { "$cond": [
                    { "$lt": [ "$date", sevenDays ] },
                    "08-15",
                    "01-07"
                ]}
            ]
        },
        "count": { "$sum": 1 },
        "totalValue": { "$sum": "$value" }
    }}
])

由于$cond是三元运算符,因此计算第一个条件以查看条件是否为真,如果为true则返回第二个参数,否则返回第三个条件为false。因此,通过在虚假案例中嵌套另一个$cond,您可以对日期所在的位置进行逻辑测试,或者“少于15天的日期”,这意味着它在最旧的范围内,或者“少于7天”,这意味着中间范围,当然还是最新的范围。

我只是在这里使用0为小于10的数字添加前缀,因此如果需要,它会为您提供一些排序,因为$group中“键”的输出本身并不存在排序。

但这就是你在单个查询中执行此操作的方法。您只需根据日期的下降位置计算出分组键的内容,并为每个键累积。

答案 1 :(得分:4)

第一步是创建代表你的范围的日期对象。让我们假设你想在8-15天前运行dange的聚合操作,这意味着你需要两个日期对象,比如说开始和结束。 start将保留一天前的日期,结束将保留8天前的日期。创建这些日期对象很容易,因为从n是前几天的日期减去n,将它们设置为之前的天数:

var start = new Date();
start.setDate(start.getDate() - 8);

var end = new Date();
end.setDate(end.getDate() - 15);

或使用.getTime()方法从时间戳毫秒减去返回标准JavaScript时间戳(自Jan 1/1970以来的毫秒数),您可以使用常规数学运算,并直接反馈到Date对象:

var today = new Date();
var start = new Date(today.getTime() - 8*24*60*60*1000);
var end = new Date(today.getTime() - 15*24*60*60*1000);

现在您已拥有日期对象,然后可以将它们用作 $match 条件,并使用 $lte $gte 比较运营商:

var pipeline = [
    {
        "$match": {
            "date": { "$lte": start, "$gte": end }
        }
    }
]

在此阶段运行聚合将为您提供日期在8-15天之前的所有文档,

db.aggregate(pipeline);

相当于 find() 查询:

db.collection.find({
    "date": { "$lte": start, "$gte": end }
});

现在,在下一个管道阶段,您需要创建一个指定组_id为null的聚合操作,使用 {计算集合中所有文档的总值和计数。 {3}} 累加器运算符:

var pipeline = [
    {
        "$match": {
            "date": { "$lte": start, "$gte": end }
        }
    },
    {
        "$group": {
            "_id": null,
            "totalValues": { "$sum": "$value" },
            "count": { "$sum": 1 }
        }
    }
]

db.collection.aggregate(pipeline);

您甚至可以进一步创建一个泛型函数,该函数返回上述聚合操作的实际总数,该操作包含两个参数:日期范围的起始值和结束:

var getTotalValues = function(start, end){
    var today = new Date();
    var startDate = new Date(today.getTime() - start*24*60*60*1000);
    var endDate = new Date(today.getTime() - end*24*60*60*1000);    

    var pipeline = [
            {
                "$match": {
                    "timestamp": { "$lte": startDate, "$gte": endDate }
                }
            },
            {
                "$group": {
                    "_id": null,
                    "totalValues": { "$sum": "$value" },            
                    "count": { "$sum": 1 }
                }
            }
        ],
        resultArray = db.collection.aggregate(pipeline).toArray();

    return resultArray[0].totalValues;
}

var total = getTotalValues(1, 8);
printjson(total); // prints the total
相关问题