计算MongoDB中每5秒的移动平均值

问题描述 投票:1回答:1

我想计算MongoDB中我的数据的移动平均值。我的数据结构如下

{
    "_id" : NUUID("54ab1171-9c72-57bc-ba20-0a06b4f858b3"),    
    "DateTime" : ISODate("2018-05-30T21:31:05.957Z"),
    "Type" : 3,
    "Value" : NumberDecimal("15.905414991993847")
}

我想在2天内和每5秒钟计算每种类型的平均值。在这种情况下,我将类型放入$match管道,但我更喜欢将结果分组为Type并将结果与​​Type分开。我做的事情如下

var start = new Date("2018-05-30T21:31:05.957Z");
var end = new Date("2018-06-01T21:31:05.957Z");
var arr = new Array();
for (var i = 0; i < 34560; i++) {               
   start.setSeconds(start.getSeconds() + 5);
   if (start <= end)
   { 
    var a = new Date(start);
    arr.push(a);   
   }
}

db.Data.aggregate([
{$match:{"DateTime":{$gte:new Date("2018-05-30T21:31:05.957Z"), 
          $lte:new Date("2018-06-01T21:31:05.957Z")}, "Type":3}},
{$bucket: {
      groupBy: "$DateTime",
      boundaries: arr,
      default: "Other",
      output: {
        "count": { $sum: 1 },
        "Value": {$avg:"$Value"}
      }
    }
}
])

看起来,它有效,但性能太慢。我怎样才能让它更快?

mongodb mongodb-query aggregation-framework
1个回答
0
投票

我再现了你描述的行为,你在数据库中进行了2天的1秒观察,而$match只需要一天的价值。如果你用60秒的时间,那么聚集就会“很好”。但是15秒花了6倍到30秒。每隔5秒钟? 144秒5秒产生17280个桶的阵列。是的。

所以我去客户端并将所有43200文档拖到客户端并创建了一个天真的线性搜索桶插槽查找器并在javascript中计算。

c=db.foo.aggregate([
{$match:{"date":{$gte:new Date(osv), $lte:new Date(endv) }}}
                ]);

c.forEach(function(r) {
    var x = findSlot(arr, r['date']);

    if(buckets[x] == undefined) {
        buckets[x] = {lb: arr[x], ub: arr[x+1], n: 0, v:0};
    }
    var zz = buckets[x];
    zz['n']++;
    zz['v'] += r['val'];
});

这实际上工作得更快,但性能顺序相同,大约92秒。

接下来,我将findSlot中的线性搜索更改为二分搜索。 5秒钟的时间从144秒变为.750秒:快了近200倍。这包括拖动43200记录并运行上面的forEach和bucketing逻辑。因此,当桶形阵列超过几百长时,$bucket可能不会使用一个很好的算法而且会受到影响。

承认这一点,我们可以使用开始时间和观察时间之间的增量的$floor来获取数据:

db.foo.aggregate([
    {$match:{"date":{$gte:now, $lte:new Date(endv) }}}

    // Bucket by turning offset from "now" into floor divided by the number           
    // of seconds of grouping.  In this way, the resulting number becomes the         
    // slot into the virtual buckets, e.g.:                                           
    // date            now            diff/1000   floor @ 5 seconds:                  
    // 1514764800000   1514764800000  0           0                                   
    // 1514764802000   1514764800000  2           0                                   
    // 1514764804000   1514764800000  4           0                                   
    // 1514764806000   1514764800000  6           1                                   
    // 1514764808000   1514764800000  8           1                                   
    // 1514764810000   1514764800000  10          2                                   
    ,{$addFields: {"ff": {$floor: {$divide: [ {$divide: [ {$subtract: [ "$date", now ]}, 1000.0 ]}, secondsBucket ] }} }}

    // Now just group by the numeric slot number!
    ,{$group: {_id: "$ff", n: {$sum:1}, tot: {$sum: "$val"}, avg: {$avg: "$val"}} }

    // Get it in 0-n order....                                                        
    ,{$sort: {_id: 1}}
                ]);

    found 17280 in 204 millis

所以我们现在有一个服务器端解决方案只需0.204秒,或快700倍。而且您不必对输入进行排序,因为$group将负责捆绑插槽号。 $sort之后的$group是可选的(但有点方便......)

© www.soinside.com 2019 - 2024. All rights reserved.