2013-04-24 56 views
7

示例文檔:MongoDB的聚合:計算運行總計從以前行的總和

{ 
_id: ObjectId('4f442120eb03305789000000'), 
time: ISODate("2013-10-10T20:55:36Z"), 
value:1 
}, 
{ 
_id: ObjectId('4f442120eb03305789000001'), 
time: ISODate("2013-10-10T28:43:16Z"), 
value:2 
}, 
{ 
_id: ObjectId('4f442120eb03305789000002'), 
time: ISODate("2013-10-11T27:12:66Z"), 
value:3 
}, 
{ 
_id: ObjectId('4f442120eb03305789000003'), 
time: ISODate("2013-10-11T10:15:38Z"), 
value:4 
}, 
{ 
_id: ObjectId('4f442120eb03305789000004'), 
time: ISODate("2013-10-12T26:15:38Z"), 
value:5 
} 

很容易獲得由日期分組的彙總結果。 但我要的是查詢返回聚集的總運行 ,類似的結果:

{ 
time: "2013-10-10" 
total: 3, 
runningTotal: 3 
}, 
{ 
time: "2013-10-11" 
total: 7, 
runningTotal: 10 
}, 
{ 
time: "2013-10-12" 
total: 5, 
runningTotal: 15 
} 

這可能與MongoDB的聚集?

+1

你可以保持運行總,你走。這將是最簡單和最有效的,尤其是因爲數據沒有變化。聚合框架是一種非常昂貴的方式來即時計算這種靜態數據。 – cirrus 2013-04-24 12:13:37

+1

目前沒有辦法用聚合框架來做到這一點。 – 2013-04-24 13:13:55

+0

@cirrus謝謝你的答案。我不太清楚那是怎麼做到的...... – user2315268 2013-04-24 13:23:39

回答

2

這就是你需要的。我已將數據中的時間標準化,以便將它們組合在一起(您可以執行諸如this之類的操作)。這個想法是$group並推動timetotal進入單獨的陣列。然後$unwindtime數組,並且您已爲每個time文檔製作了totals數組的副本。然後,您可以從包含所有不同時間的數據的數組中計算出runningTotal(或類似於滾動平均值)。 $unwind生成的'索引'是對應於timetotal的數組索引。在$unwind之前對$sort非常重要,因爲這可以確保數組的順序正確。

db.temp.aggregate(
    [ 
     { 
      '$group': { 
       '_id': '$time', 
       'total': { '$sum': '$value' } 
      } 
     }, 
     { 
      '$sort': { 
       '_id': 1 
      } 
     }, 
     { 
      '$group': { 
       '_id': 0, 
       'time': { '$push': '$_id' }, 
       'totals': { '$push': '$total' } 
      } 
     }, 
     { 
      '$unwind': { 
       'path' : '$time', 
       'includeArrayIndex' : 'index' 
      } 
     }, 
     { 
      '$project': { 
       '_id': 0, 
       'time': { '$dateToString': { 'format': '%Y-%m-%d', 'date': '$time' } }, 
       'total': { '$arrayElemAt': [ '$totals', '$index' ] }, 
       'runningTotal': { '$sum': { '$slice': [ '$totals', { '$add': [ '$index', 1 ] } ] } }, 
      } 
     }, 
    ] 
); 

我在〜80 000個文檔的集合上使用了類似的東西,聚合到63個結果。我不確定它能在大型集合上運行得如何,但是我發現,一旦數據減少到可管理的大小,對聚合數據執行轉換(預測,數組操作)似乎沒有很大的性能成本。

0

這裏是另一種方法

管道

db.col.aggregate([ 
    {$group : { 
     _id : { time :{ $dateToString: {format: "%Y-%m-%d", date: "$time", timezone: "-05:00"}}}, 
     value : {$sum : "$value"} 
    }}, 
    {$addFields : {_id : "$_id.time"}}, 
    {$sort : {_id : 1}}, 
    {$group : {_id : null, data : {$push : "$$ROOT"}}}, 
    {$addFields : {data : { 
     $reduce : { 
      input : "$data", 
      initialValue : {total : 0, d : []}, 
      in : { 
       total : {$sum : ["$$this.value", "$$value.total"]},     
       d : {$concatArrays : [ 
         "$$value.d", 
         [{ 
          _id : "$$this._id", 
          value : "$$this.value", 
          runningTotal : {$sum : ["$$value.total", "$$this.value"]} 
         }] 
       ]} 
      } 
     } 
    }}}, 
    {$unwind : "$data.d"}, 
    {$replaceRoot : {newRoot : "$data.d"}} 
]).pretty() 

收集

> db.col.find() 
{ "_id" : ObjectId("4f442120eb03305789000000"), "time" : ISODate("2013-10-10T20:55:36Z"), "value" : 1 } 
{ "_id" : ObjectId("4f442120eb03305789000001"), "time" : ISODate("2013-10-11T04:43:16Z"), "value" : 2 } 
{ "_id" : ObjectId("4f442120eb03305789000002"), "time" : ISODate("2013-10-12T03:13:06Z"), "value" : 3 } 
{ "_id" : ObjectId("4f442120eb03305789000003"), "time" : ISODate("2013-10-11T10:15:38Z"), "value" : 4 } 
{ "_id" : ObjectId("4f442120eb03305789000004"), "time" : ISODate("2013-10-13T02:15:38Z"), "value" : 5 } 

結果

{ "_id" : "2013-10-10", "value" : 3, "runningTotal" : 3 } 
{ "_id" : "2013-10-11", "value" : 7, "runningTotal" : 10 } 
{ "_id" : "2013-10-12", "value" : 5, "runningTotal" : 15 } 
>