2015-07-21 114 views
3

我有存儲在蒙戈集合,其包括基本信息作爲REQUEST_ID和將其加入到集合中的時間,例如一些日誌數據:蒙戈聚合

{ 
    "_id" : ObjectId("55ae6ea558a5d3fe018b4568"), 
    "request_id" : "030ac9f1-aa13-41d1-9ced-2966b9a6g5c3", 
    "time" : ISODate("2015-07-21T16:00:00.00Z") 
} 

我想知道是否我可以使用聚合框架來聚合一些統計數據。我希望獲得在最近X小時的每個N分鐘間隔內創建的對象的計數。

所以,我需要10周分鐘的時間間隔過去1小時輸出應該是這樣的:

{ "_id" : 0, "time" : ISODate("2015-07-21T15:00:00.00Z"), "count" : 67 } 
{ "_id" : 0, "time" : ISODate("2015-07-21T15:10:00.00Z"), "count" : 113 } 
{ "_id" : 0, "time" : ISODate("2015-07-21T15:20:00.00Z"), "count" : 40 } 
{ "_id" : 0, "time" : ISODate("2015-07-21T15:30:00.00Z"), "count" : 10 } 
{ "_id" : 0, "time" : ISODate("2015-07-21T15:40:00.00Z"), "count" : 32 } 
{ "_id" : 0, "time" : ISODate("2015-07-21T15:50:00.00Z"), "count" : 34 } 

我會用它來獲取數據的圖表。

任何意見是讚賞!

回答

1

這樣的事情?

pipeline = [ 
    {"$project": 
     {"date": { 
      "year": {"$year": "$time"}, 
      "month": {"$month": "$time"}, 
      "day": {"$dayOfMonth": "$time"}, 
      "hour": {"$hour": "$time"}, 
      "minute": {"$subtract": [ 
       {"$minute": "$time"}, 
       {"$mod": [{"$minute": "$time"}, 10]} 
      ]} 
     }} 
    }, 
    {"$group": {"_id": "$date", "count": {"$sum": 1}}} 
] 

實施例:

> db.foo.insert({"time": new Date(2015, 7, 21, 22, 21)}) 
> db.foo.insert({"time": new Date(2015, 7, 21, 22, 23)}) 
> db.foo.insert({"time": new Date(2015, 7, 21, 22, 45)}) 
> db.foo.insert({"time": new Date(2015, 7, 21, 22, 33)}) 
> db.foo.aggregate(pipeline) 

和輸出:

{ "_id" : { "year" : 2015, "month" : 8, "day" : 21, "hour" : 20, "minute" : 40 }, "count" : 1 } 
{ "_id" : { "year" : 2015, "month" : 8, "day" : 21, "hour" : 20, "minute" : 20 }, "count" : 2 } 
{ "_id" : { "year" : 2015, "month" : 8, "day" : 21, "hour" : 20, "minute" : 30 }, "count" : 1 } 
0

代替具體的答案的一個指針。你可以很容易地使用date aggregations分鐘,幾小時和給定的時間。每10分鐘會有點棘手,但可能有一些爭論。儘管如此,由於大型數據集的堅固性,聚合速度會很慢。

我建議提取分鐘後插入

{ 
    "_id" : ObjectId("55ae6ea558a5d3fe018b4568"), 
    "request_id" : "030ac9f1-aa13-41d1-9ced-2966b9a6g5c3", 
    "time" : ISODate("2015-07-21T16:00:00.00Z"), 
    "minutes": 16 
} 

即使它聽起來荒謬絕倫增加位數和sextiles或任何N個可能。

{ 
    "_id" : ObjectId("55ae6ea558a5d3fe018b4568"), 
    "request_id" : "030ac9f1-aa13-41d1-9ced-2966b9a6g5c3", 
    "time" : ISODate("2015-07-21T16:00:00.00Z"), 
    "minutes": 16, 
    "quartile: 1, 
    "sextile: 2, 
} 

第一次嘗試做的分鐘$div。不做細胞和地板。但檢查出

Is there a floor function in Mongodb aggregation framework?

+0

['$ mod'](http://docs.mongodb.org/manual/reference/operator/aggregation/mod/)運算符很容易做到這一點。 –

1

有一對夫婦的接近這取決於其輸出格式最適合您需要的方法。主要的注意事項是,使用"aggregation framework"本身,您實際上不能返回「投射」日期,但可以在處理API中的結果時獲取易於重構爲對象的值。

第一種方法是使用現有的"Date Aggregation Operators"到聚合框架:

db.collection.aggregate([ 
    { "$match": { 
     "time": { "$gte": startDate, "$lt": endDate } 
    }}, 
    { "$group": { 
     "_id": { 
      "year": { "$year": "$time" }, 
      "dayOfYear": { "$dayOfYear": "$time" }, 
      "hour": { "$hour": "$time" }, 
      "minute": { 
       "$subtract": [ 
        { "$minute": "$time" }, 
        { "$mod": [ { "$minute": "$time" }, 10 ] } 
       ] 
      } 
     }, 
     "count": { "$sum": 1 } 
    }} 
]) 

它返回一個包含所有你想要的「日期」的值_id複合鍵。或者,如果只是在「小時」內,那麼只需使用「分鐘」部分,並根據您的量程選擇的startDate計算實際日期。

或者您可以使用普通的「日期數學」來獲得自「時代」以來可以再次被直接饋送到日期構造器的毫秒數。

db.collection.aggregate([ 
    { "$match": { 
     "time": { "$gte": startDate, "$lt": endDate } 
    }}, 
    { "$group": { 
     "_id": { 
      "$subtract": [ 
       { "$subtract": [ "$time", new Date(0) ] }, 
       { "$mod": [ 
        { "$subtract": [ "$time", new Date(0) ] }, 
        1000 * 60 * 10 
       ]} 
      ] 
     }, 
     "count": { "$sum": 1 } 
    }} 
]) 

在任何情況下,你想做的事是實際應用$group之前使用$project。作爲一個「流水線階段」,$project必須「循環」所有選定的文件並「轉換」內容。

這需要時間,並且增加了查詢的執行總數。您可以直接申請$group,如圖所示。因爲JavaScript函數實際上允許重鑄爲日期,但比聚合框架要慢,當然也可以不用,但是,如果你真的「純」了一個Date對象而沒有後處理,那麼你總是可以使用"mapReduce"。光標響應:

db.collection.mapReduce(
    function() { 
     var date = new Date(
      this.time.valueOf() 
      - (this.time.valueOf() % (1000 * 60 * 10)) 
     ); 
     emit(date,1); 
    }, 
    function(key,values) { 
     return Array.sum(values); 
    }, 
    { "out": { "inline": 1 } } 
) 

最好的辦法是使用雖然聚集,作爲改造的響應是很容易的:

db.collection.aggregate([ 
    { "$match": { 
     "time": { "$gte": startDate, "$lt": endDate } 
    }}, 
    { "$group": { 
     "_id": { 
      "year": { "$year": "$time" }, 
      "dayOfYear": { "$dayOfYear": "$time" }, 
      "hour": { "$hour": "$time" }, 
      "minute": { 
       "$subtract": [ 
        { "$minute": "$time" }, 
        { "$mod": [ { "$minute": "$time" }, 10 ] } 
       ] 
      } 
     }, 
     "count": { "$sum": 1 } 
    }} 
]).forEach(function(doc) { 
    doc._id = new Date(doc._id); 
    printjson(doc); 
}) 

然後你有你的間隔將輸出與真實的Date對象分組。

+0

在實際項目之前使用組的好技巧,很高興知道如果在組之前使用了所有文檔,項目正在循環。我希望在這裏實現的一件事(我更新了我的問題)實際上是在空間間隔(不包含任何對象的間隔)結果中計數爲0,因爲我將這些傳遞給圖框架。這可以節省解析的時間,並在使用數據的實際代碼中添加該時間。這是可以實現的嗎? –

+0

@JeromeWalters真的不是明智的做法。如果你仔細想想,你只能對「存在」的鍵進行「求和」。所以儘管在範圍內投入每個「可能的」鍵是「可能的」,但它實際上並不實用,因爲您正在拋出一個包含所有這些鍵的變量。聰明的錢是在你的API代碼中將「結合」到空白的預期範圍,而不是在服務器上「扔」所有這些值。同樣適用於同一事物的SQL變體。只因爲你「可以」並不意味着你「應該」。 –

+0

@JeromeWalters也「改變你的問題」,所以它與你問的內容本質上是「不同的」。這裏收到的不好。如果一個答案引導你另一個問題,那麼改爲「問另一個問題」。 –