Group By MongoDb上的聚合

时间:2016-01-22 13:41:07

标签: mongodb aggregation-framework

我使用的是mongodb 2.6。我以这种形式存储了我的数据:

{ 
    "_id" : "my-sensor-1", 
    "points": [ 
        { "timeStamp" : NumberLong("1453468362174"), "value" : 41 }, 
        { "timeStamp" : NumberLong("1453468483297"), "value" : 66 }, 
        { "timeStamp" : NumberLong("1453468485568"), "value" : 49 },
         ...    
    ] 
}

为了聚合文档,我做了这样的查询:

db.pointsTest.aggregate([
    { $match: { $and: [ {"points.timeStamp" : { $gt : 1453433925163}}, 
                        {"_id":"my-sensor-10"} ] } },
    {"$unwind":"$points"},
    {$group: {_id: "my-sensor-1","average":{$avg : "$points.value"}}}
])
  

{" _id" :" my-sensor-1","平均" :52}

结果

我已将时间戳存储为毫秒,因此每次我想要聚合特定时间间隔时,我必须更改timeStamp值的界限。

我怎样才能在一个时间段内进行聚合,并按间隙间隔对结果进行分组(即将现在的平均值()-1day GROUP乘以1h)?

修改

我想做这样的事情:

db.pointsTest.aggregate([
    { $match: { $and: [ {"points.timeStamp" : { $gt : 1453433925163, $lt : 1453555555555}}, {"_id":"my-sensor-10"} ] } }, {"$unwind":"$points"}, {$group: {_id: "my-sensor-1","average":{$avg : "$points.value"}, ???}}
])

,结果将是此时间间隔的平均值,按1h分组。 假设我想要汇总12月31日每小时的平均值:

  

时间间隔31/12/2015 20:00:00,平均值:xyz

     

时间间隔31/12/2015 21:00:00,平均值:xyz

此时为了实现这一点,我必须以1小时的间隔分割时间间隔并向数据库发出多个请求。

I.E使用InfluxDB做同样的事情我这样做:

"SELECT MEAN(value) From myMeasures where key='my-sensor-1' and time > now() - 1d GROUP BY time(1h)"

3 个答案:

答案 0 :(得分:2)

您需要在Mongo查询中进行一些数学计算,以根据不同的时间间隔对数据进行分组。

$ subtract和$ mod的组合将帮助您按特定时间间隔对数据进行分组。

查询如下:

db.sensor.aggregate({
    $match: {
    $and: [{
        "points.timeStamp": {
            $gt: 1453433925163,
            $lt: 1453555555555
        }
    }, {
        "_id": "my-sensor-1"
    }]
    }
}, {
    $unwind: "$points"
}, {
    "$group": {
    "_id": {
        "$subtract": ["$points.timeStamp", {
            "$mod": ["$points.timeStamp", 1000 * 60]
        }]
    },
    "average": {
        "$avg": "$points.value"
    }
    }
})

希望,这对你有所帮助。

答案 1 :(得分:1)

要在间隙间隔中获得结果,您可以使用javascript函数来支持您的查询,因为mongodb shell支持它们:

要使用您提供的示例执行示例,您希望从$match获得now()-1day值,您可以在汇总之前执行此操作:

var now = new Date();
var yesterday = new Date();
// using getHours and setHours, since the Date object doesnt have method for getDays and setDays
yesterday.setHours(now.getHours() - 24);

yesterday.getTime()将以milis格式生成日期,您可以在$match阶段的汇总中使用该日期

db.pointsTest.aggregate([
    { $match: { $and: [ {"points.timeStamp" : { $gt : yesterday.getTime()}}, 
                        {"_id":"my-sensor-10"} ] } },

现在,要按小时间隔对结果进行分组,我会在$project之前添加$group阶段,以添加一个新字段,其中计算每小时间隔,{{1}你将获得初始时间和输入时间之间的总差异毫秒数,然后将其转换为小时数并向上舍入到下一个整数值。

最后,使用points.timeStamp - yesterday.getTime()阶段的新字段,用于$project阶段。

答案 2 :(得分:1)

您可以使用mongodb mapReduce轻松完成。

请尝试以下代码:

// generate a query to filter result by date and _id.
// be aware that our query matches documents that contain an array field with 
// at least one element that matches all the specified criteria.
var yesterday = new Date();
yesterday.setDate(yesterday.getDate()-1);
var query = {"points.timeStamp" : { $gt : yesterday.getTime()}, "_id":"my-sensor-1"};

var map = function(){
    var points = this.points;
    for(var i=0;i<points.length;i++){
        var date = new Date(points[i].timeStamp);

        //remove minutes, seconds and milliseconds from the date and emit it
        date.setHours(date.getHours(), 0, 0, 0);
        emit(date, points[i].value);
    }
};

var reduce = function(key, values){
    //calculate average
    var total = 0;
    for(var i = 0; i < values.length; i++) {
        total += values[i];
    }
    var avg = total / values.length;
    return avg;
};

db.pointsTest.mapReduce(map, reduce, {out:{inline: 1}, query: query})