mongodb 以 10 分钟为间隔汇总时间戳

mongodb aggregrate timestamp by 10 minute intervals

我正在尝试将与查询匹配的大型数据集分组为 10 分钟 "time slices"。我想知道是否有任何方法可以使用 mongodb 的聚合函数有效地做到这一点。

我有一个文档,如下所示:

{
    _id: ObjectID(""),
    groupID: '1234',
    name: 'dataPointName',
    timestamp: 1432765200000,
    value: 1234
}

然后我想在称为 "grouped_data" 的 10 分钟组间隔中对一组 [timestamp,value] 对进行分组。我想知道是否有一种有效的方法来执行所有这些操作?

这显然是一个可以使用 map-reduce 轻松解决的问题。您在此处的密钥将 timestamp / (10*60*1000) 四舍五入到最大的较低整数。你只需要在减少步骤中聚合你的grouped_data

然而,这有点复杂,因为我假设您需要让您的值按时间戳排序(记住 reduce function should be commutative)。为了在这里提供帮助,我将使用 finalizer 对结果进行排序。

map = function() {
  window_width = 10*60*1000
  emit(Math.floor(this.timestamp/window_width),
       { grouped_data: [[ this.timestamp, this.value]] })
}

// aggregates values by concatenating the [[timestamp, values]] arrays
// don't bother sorting here as this will be done by the finalizer
reduce = function(key, values) {
  return values.reduce(
            function(a,b) { return { grouped_data: a.grouped_data.concat(b.grouped_data)} }
      )
} 

// Sort data in each array by timestamp
finalize = function(key, reducedValue) {
  return { grouped_data: reducedValue.grouped_data.sort(function(a,b) { a[0] - b[0] }) }
}

正在制作(使用一些虚拟数据集):

> db.w.mapReduce(map, reduce, { finalize: finalize, out: {inline:1}}).results
[
    {
        "_id" : 2387925,
        "value" : {
            "grouped_data" : [
                [
                    1432755300001,
                    1234
                ],
                [
                    1432755300000,
                    1234
                ]
            ]
        }
    },
    {
        "_id" : 2387942,
        "value" : {
            "grouped_data" : [
                [
                    1432765200000,
                    1234
                ],
                [
                    1432765200001,
                    1234
                ],
                [
                    1432765300000,
                    1234
                ],
                [
                    1432765300001,
                    1234
                ]
            ]
        }
    }
]

您可以先投影一个新的日期字段,然后您可以按时间间隔对其进行分组。

假设您有以下测试文档:

db.collection.insert([
    {
        groupID: '1234',
        name: 'dataPointName',
        timestamp: 1432765200000,
        value: 1234
    },
    {
        groupID: '1234',
        name: 'dataPointName',
        timestamp: 1432765300000,
        value: 1234
    },
    {
        groupID: '1234',
        name: 'dataPointName',
        timestamp: 1432766100000,
        value: 1234
    }
])

然后您可以实现以下聚合:

db.collection.aggregate([
    {
        "$project": {
            "date": { "$add": [new Date(0), "$timestamp"] },
            "timestamp": 1,
            "value": 1
        }
    },
    { 
        "$group": {
            "_id": {
                "year": { "$year": "$date" },
                "dayOfYear": { "$dayOfYear": "$date" },
                "interval": {
                    "$subtract": [ 
                        { "$minute": "$date" },
                        { "$mod": [{ "$minute": "$date"}, 10 ] }
                    ]
                }
            },
            "grouped_data": { "$push": {"timestamp": "$timestamp", "value": "$value" } }
        }
    },
    {
        "$project":{
            "_id": 0,
            "grouped_data": 1
        }
    }
])

输出:

/* 0 */
{
    "result" : [ 
        {
            "grouped_data" : [ 
                {
                    "timestamp" : 1432766100000,
                    "value" : 1234
                }
            ]
        }, 
        {
            "grouped_data" : [ 
                {
                    "timestamp" : 1432765200000,
                    "value" : 1234
                }, 
                {
                    "timestamp" : 1432765300000,
                    "value" : 1234
                }
            ]
        }
    ],
    "ok" : 1
}

-- 编辑 --

要将数据格式化为类似于 [timestamp,value] 的数组而不是 key/value 数组,您可以使用聚合游标的 forEach() 方法,如下所示:

var result = [];   
db.collection.aggregate(pipeline).forEach(function (doc){    
    data = []; 
    doc.grouped_data.forEach(function (obj){               
        data.push(obj.timestamp);
        data.push(obj.value);        
    });
    result.push(data);
})

printjson(result);

输出

[
        [
                1432766100000,
                1234
        ],
        [
                1432765200000,
                1234,
                1432765300000,
                1234
        ]
]