2015-02-05 62 views
1

我正在尝试检测集合中某个值的“趋势”。MongoDB:有没有办法使用聚合检测价值趋势?

比方说,我有以下几点:

{ created_at: 2014-12-01, value:1015 } 
{ created_at: 2014-12-01, value:1015 } 
{ created_at: 2014-12-01, value:1019 } 
{ created_at: 2014-12-02, value:1018 } 
{ created_at: 2014-12-02, value:1021 } 
{ created_at: 2014-12-03, value:1010 } 
{ created_at: 2014-12-03, value:1012 } 
{ created_at: 2014-12-03, value:1011 } 
{ created_at: 2014-12-04, value:1012 } 

我只是想有一个像输出:

{ created_at: 2014-12-01, average: 1016, diff: 0} 
{ created_at: 2014-12-02, average: 1019, diff: 3} 

用Diff是平均值的 两个日期之间的差异。

我想通了如何计算平均值,发现最小/最大和第一/最后的值的集合中,但不能找到一种方法,两个平均值比较...

+0

计算的平均差异会更好。目前可能无法使用聚合框架。 – BatScream

+0

你不能实事求是地计算一样,与聚合框架(一个或多个管道)输出,但有很多,你可以计算代替。我想更多的解释你如何以及为什么检测趋势将是帮助我们提供良好建议的最佳方式。 – wdberkeley

+0

@wdberkeley我有一个从传感器网络持有的读数集合(名为“sensor_readings”)。读数随机出现,并按照您在示例中看到的方式进行存储 - 只需添加完整的时间戳即可。每10分钟我想汇总数据,每分钟对它们进行分组,计算分钟平均值和前一分钟的差值。 –

回答

0

粗线条:我愿意计算平均值的十分钟内:

> var avgCursor = db.sensor_readings.aggregate([ 
    { "$match" : { "created_at" : { "$gt" : ten_minutes_ago, "$lte" : now } } } 
    { "$group" : { "_id" : 0, "average" : { "$avg" : "$value" } } } 
]} 
> var avgDoc = avgCursor.toArray()[0] 
> avgDoc 
{ "_id" : 0, "average" : 23 } 

然后我将其存储在另一个集合:

> db.sensor_averages.insert({ "start" : ten_minutes_ago, "end" : now, "average" : avgDoc.average }) 

最后,记得你需要计算differenc两个平均值e和计算它:

> var diffCursor = db.sensor_averages.find({ "start" : { "$gte" : twenty_minutes_ago } }).sort({ "start" : -1 }) 
> var diffArray = diffCursor.toArray() 
> var difference = diffArray[0].average - diffArray[1].average 

你也可以跳过定期聚集,而是保持运行平均更新sensor_averages,跳跃到一个新的文档每次10分钟。在每次10分钟的时间内开始,插入sensor_averages一个文档

{ 
    "start" : now, 
    "svalues" : 0, 
    "nvalues" : 0 
} 

然后在sensor_reading文档的每个刀片在接下来的十几分钟,还更新了sensor_averages DOC:

db.sensor_averages.update(
    { "start" : now_rounded_to_the_ten_minute_boundary }, 
    { "$inc" : { "svalues" : value, "nvalues" : 1 } } 
) 

然后,当你想要求平均值之间的差异时,回想一下适当的两篇文档,将svalues除以nvalues得到平均值,然后减去。

+0

今天尝试这个并发布更新。日Thnx –

1

假设这些是根据您对“每分钟聚合”的评论的实际日期,唯一真正的方法是在一次传递中使用mapReduce。这里的关键是,MapReduce的可存储一个全局变量,因此“跟踪”您的最后结果,以确定每个聚合记录

db.collection.mapReduce(
    function() { 
     // Round date to the minute 
     var key = this.created_at.valueOf() 
      - (this.created_at.valueOf() % (1000 * 60)); 
     emit(key, { "average": this.value }); 
    }, 
    function(key,values) { 
     values = values.map(function(i) { return i.average }); 
     var result = { 
      "average": Math.floor(Array.avg(values)) 
     }; 
     return result; 
    }, 
    { 
     "out": { "inline": 1 }, 
     "scope": { "lastAvg": 0 }, 
     "finalize": function(key,value) { 
      value.diff = (lastAvg == 0) ? 0 : value.average - lastAvg; 
      lastAvg = value.average; 
      return value; 
     } 
    } 
) 

或者你可以为已经提到“后处理”之间的“差异”并在您的客户端代码中执行相同的操作,以便在您使用类似的作用域变量迭代游标时计算差异。作为外壳例如:

var lastAvg = 0; 
db.collection.aggregate([ 
    { "$group": { 
     "_id": { "$subtract": [ 
      { "$subtract": [ "$created_date", new Date(0) ] }, 
      { "$mod": [ 
       { "$subtract": [ "$created_date", new Date(0) ] }, 
       1000 * 60 
      ]} 
     ]}, 
     "average": { "$avg": "$value" } 
    }}, 
    { "$sort": { "_id": 1 } } 
]).forEach(function(doc) { 
    doc.average = Math.floor(doc.average); 
    doc.diff = (lastAvg == 0) ? 0 : doc.average - lastAvg; 
    lastAvg = doc.average; 
    printjson(doc); 
}) 

在这两种情况下,我以日期对象转换为UNIX /纪元时间戳表示为被杀进了它的一些使用日期数学原理是通过微小最近的楼层。使用汇总框架,您可以交替使用date aggregation operators来提取分组的日期部分。

在这两种情况下,将对象重新投射到内部.mapReduce()或使用.aggregate()进行后期处理是非常简单的。

所以在收官之作,您可以使用的MapReduce的“全球范围”功能,也可以直接处理来自聚集而产生光标,以制定出各分组结果之间的差异。如果在应用程序代码做

+0

尼尔,您可以显示如何做到这一点弹簧数据的MongoDB? – chiku

相关问题