MongoDb上的Group By聚合操作

6

我使用的是 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" : "我的传感器-1", "平均值" : 52 }

结果

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

如何在时间段上进行聚合,并按间隔(即从now() -1day聚合平均值 GROUP by 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"}, ???}}
])

并且结果将会是按每小时分组的该时间间隔均值。假设我想要聚合12月31日每个小时的平均值:

时间间隔的时间戳为 2015年12月31日20:00:00,平均值为:xyz

时间间隔的时间戳为 2015年12月31日21:00:00,平均值为:xyz

目前为止,为了实现这一点,我必须将时间间隔分成1小时的间隔,并向数据库发出多个请求。
例如使用InfluxDB完成相同的操作,我会这样做:
"SELECT MEAN(value) From myMeasures where key='my-sensor-1' and time > now() - 1d GROUP BY time(1h)"
3个回答

3

您需要在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"
    }
    }
})

希望这对你有所帮助。

非常有用,但我认为它缺少了一些东西(也许我在我的问题中没有指定)。为了迭代列表中的项目,我不得不添加另一个$match,因为当条件为真时,它会返回整个项目。因此查询如下: db.pointsTest.aggregate({ $match: { $and: [{ "points.timeStamp": { $gte: 1453797806927 } }, { "_id": "my-sensor-1" }] } }, { $unwind: "$points" }, {$match :{ "points.timeStamp":{$gte: 1453797806927 }}} ,{ "$group": { "_id": { "$subtract": ["$points.timeStamp", { "$mod": ["$points.timeStamp", 1000 * 60]}]},"average":{"$avg":"$points.value"}}}) - bill

1
为了在间隔中获取结果,您可以利用JavaScript函数来支持您的查询,因为MongoDB shell支持它们:
要执行您提供的示例,其中您想要从“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()将返回日期的毫秒表示,您可以在$match阶段中使用它进行聚合。

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

现在要按小时间隔分组结果,我会在$group之前添加一个$project阶段,以添加一个新字段,计算小时间隔,使用points.timeStamp - yesterday.getTime()获取初始时间和输入时间之间的总毫秒差,将其转换为小时并向上舍入到下一个整数值。最后,在$group阶段中使用$project阶段的新字段。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接