Group通过在MongoDb上的聚合
我使用的是mongodb 2.6。我已经存储在这个形式我的数据:Group通过在MongoDb上的聚合
{
"_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()-1天的聚合平均值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分组在此时间间隔的平均值。 假设欲聚合的平均值为十二月31的每隔一小时:
的间隔31/12/2015 20点00分00秒,平均时间戳:的间隔31的xyz
时间戳/二千零十五分之一十二21:00:00,平均:XYZ
此时为了达到我要的时间间隔1小时间隔分割并进行多次数据库请求。使用InfluxDB做我做同样的
IE:
"SELECT MEAN(value) From myMeasures where key='my-sensor-1' and time > now() - 1d GROUP BY time(1h)"
您需要在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"
}
}
})
希望,这将有利于你。
要获得间隙间隔的结果,你可以使用JavaScript函数来支持你的查询,因为MongoDB的shell支持他们:
执行与示例中的例子,你提供的,要$match
值从now()-1day
,您可以在聚合之前:
yesterday.getTime()
将产生日期milis,您可以在$match
阶段
db.pointsTest.aggregate([
{ $match: { $and: [ {"points.timeStamp" : { $gt : yesterday.getTime()}},
{"_id":"my-sensor-10"} ] } },
我们在每小时一班分组结果用它在你的聚集,我会在$group
前添加$project
阶段,加一个新的字段,其中小时间隔计算,points.timeStamp - yesterday.getTime()
你会得到初始时间和输入时间之间的总差异毫秒数,你将其转换为小时,然后取整到下一个整数值。
最后,使用$project
阶段中的新字段用于$group
阶段。
你可以很容易地用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})
真正有用的,但我认为这是失去了一些东西(也许我没有具体说明它在我的问题)。为了迭代列表中的项目,我必须添加另一个$匹配项,因为当条件为真时,它会返回整个项目。所以查询是这样的: 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