2014-06-19 40 views
5

我正在使用mongodb聚合來聚合數據集。我的情況有點複雜。我有一個集合如下:Mongodb聚集按天然後小時

{ 
    startTime: ISODate("2014-12-31T10:20:30Z"), 
    customerId: 123, 
    ping: "2", 
    link: "3" 
} 

現在我要彙總數據,另一個收集如下:

{ 
_id: { 
day: ISODate("2014-12-31T00:00:00Z"), 
customerId: 123 
}, 
hours: [ 
    { 
    hour: ISODate("2014-12-31T10:00:00Z"), 
    pings: 2, 
    links: 3 
    }, 
    { 
    hour: ISODate("2014-12-31T11:00:00Z"), 
    pings: 5, 
    links: 6 
    } 
] 
} 

正如你所看到的數據是按照日期分組,然後再由小時。我有以下的聚合查詢來將它們分組,但是如何將它們按小時進一步分組?任何想法?

var pipeline = [ 
{ 
$project : { 
     startTime : 1, 
       customerId: 1, 
     ping:1, 
     link:1, 
     date : "$startTime", 
     h : { 
      "$hour" : "$startTime" 
     }, 
     m : { 
      "$minute" : "$startTime" 
     }, 
     s : { 
      "$second" : "$startTime" 
     }, 
     ml : { 
      "$millisecond" : "$startTime" 
     } 
    } 
}, 
{ 
$project: { 
    startTime : 1, 
      customerId: 1, 
    ping:1, 
    link:1, 
     date : {  
      "$subtract" : [  
       "$date",  
       {  
         "$add" : [  
          "$ml",  
          {  
           "$multiply" : [  
            "$s",  
            1000  
           ]  
          },  
          {  
           "$multiply" : [  
            "$m",  
            60,  
            1000  
           ]  
          }, 
          {  
           "$multiply" : [  
            "$h",  
            60,  
            60,  
            1000 
           ]  
          }  
         ]  
       }  
      ]  
     } 
    }   
}, 
{ 
    $match: { 
     "startTime": { 
      $gte: new ISODate("2013-12-01T07:00:00Z"), 
      $lte: new ISODate("2014-01-01T08:00:00Z"), 
     } 
    } 
}, 
// Aggregate the data 
{ 
    $group: { 
     _id: {day : "$date", customerId: "$customerId"}, 
     pings : {$sum: "$ping"}, 
     links : {$sum: "$links"} 
    } 
} 
]; 

回答

7

,你基本上要的是一個雙重分組,但你不回來使用date aggregation operators,只是相關部分獲取整個Date對象:

db.collection.aggregate([ 
    { "$group": { 
     "_id": { 
      "customerId": "$customerId", 
      "day": { "$dayOfYear": "$startTime" }, 
      "hour": { "$hour": "$startTime" } 
     }, 
     "pings": { "$sum": "$ping" }, 
     "links": { "$sum": "$link" } 
    }}, 
    { "$group": { 
     "_id": { 
      "customerId": "$_id.customerId", 
      "day": "$_id.day" 
     }, 
     "hours": { 
      "$push": { 
       "hour": "$_id.hour", 
       "pings": "$pings", 
       "links": "$links" 
      } 
     } 
    }} 
]) 

$group給你通過每天將結果放入一個數組中來設置您想要的格式。樣品中的單個文件,但你基本上得到的結果是這樣的:如果你找到爲止運營商難以處理或想爲Date對象簡化的「直通」結果的結果

{ 
    "_id" : { 
      "customerId" : 123, 
      "day" : 365 
    }, 
    "hours" : [ 
      { 
        "hour" : 10, 
        "pings" : 2, 
        "links" : 3 
      } 
    ] 
} 

,然後你可以投作爲劃時代的時間戳來代替:

db.collection.aggregate([ 
    { "$group": { 
     "_id": { 
      "customerId": "$customerId", 
      "day": { 
       "$subtract": [ 
        { "$subtract": [ "$startTime", new Date("1970-01-01") ] }, 
        { 
         "$mod": [ 
          { "$subtract": [ "$startTime", new Date("1970-01-01") ] }, 
          1000*60*60*24 
         ] 
        } 
       ] 
      }, 
      "hour": { 
       "$subtract": [ 
        { "$subtract": [ "$startTime", new Date("1970-01-01") ] }, 
        { 
         "$mod": [ 
          { "$subtract": [ "$startTime", new Date("1970-01-01") ] }, 
          1000*60*60 
         ] 
        } 
       ] 
      } 
     }, 
     "pings": { "$sum": "$ping" }, 
     "links": { "$sum": "$link" } 
    }}, 
    { "$group": { 
     "_id": { 
      "customerId": "$_id.customerId", 
      "day": "$_id.day" 
     }, 
     "hours": { 
      "$push": { 
       "hour": "$_id.hour", 
       "pings": "$pings", 
       "links": "$links" 
      } 
     } 
    }} 
]) 

的竅門在那裏是當你從另一個$subtract一個約會對象,你得到了「時代」的值返回結果。在這種情況下,我們使用「epoch」開始日期來獲取整個時間戳值,並提供「日期數學」來將時間修正爲所需的時間間隔。所以結果如下:

{ 
    "_id" : { 
      "customerId" : 123, 
      "day" : NumberLong("1419984000000") 
    }, 
    "hours" : [ 
      { 
        "hour" : NumberLong("1420020000000"), 
        "pings" : 2, 
        "links" : 3 
      } 
    ] 
} 

這可能比日期運營商根據您的需求提供的結果更可口。

您可以用MongoDB的2.6通過$let操作,使您的作用域的操作聲明「變量」,也加一點簡寫本:

db.event.aggregate([ 
    { "$group": { 
     "_id": { 
      "$let": { 
       "vars": { 
        "date": { "$subtract": [ "$startTime", new Date("1970-01-01") ] }, 
        "day": 1000*60*60*24, 
        "hour": 1000*60*60 
       }, 
       "in": { 
        "customerId": "$customerId", 
        "day": { 
         "$subtract": [ 
          "$$date", 
          { "$mod": [ "$$date", "$$day" ] } 
         ] 
        }, 
        "hour": { 
         "$subtract": [ 
          "$$date", 
          { "$mod": [ "$$date", "$$hour" ] } 
         ] 
        } 
       } 
      } 
     }, 
     "pings": { "$sum": "$ping" }, 
     "links": { "$sum": "$link" } 
    }}, 
    { "$group": { 
     "_id": { 
      "customerId": "$_id.customerId", 
      "day": "$_id.day" 
     }, 
     "hours": { 
      "$push": { 
       "hour": "$_id.hour", 
       "pings": "$pings", 
       "links": "$links" 
      } 
     } 
    }} 
]) 

此外,我幾乎忘了提,你的「平值「和」鏈接「實際上是字符串,除非這是一個錯字。但是,如果沒有,那麼確保您先將它們轉換爲數字。

+0

謝謝。你能幫我進一步嗎?我有一個調度程序,每小時運行一次,以聚合上一小時的數據,如下所示:'{「_id」:{customerId「:123,」day「:ISODate(」2012-06-20:00:00 :00Z「)}, 」hours「:[{」hour「:ISODate(」2012-06-20:01:00:00Z「),」ping「:2,」links「:3}]}'After上面運行聚合查詢,我需要合併/插入到這個集合來存儲聚合數據。我怎樣才能做到這一點?謝謝 – user3756522

+0

@ user3756522這真的聽起來像是另一個問題,最好通過一個新的帖子問你,因爲你正確地解釋你的意圖而不是評論。答案中顯示的查詢將根據您發送的範圍每天和每小時彙總一次。此外,您的'$ match'應該始終是管道**的第**個**階段。使用MongoDB 2.6,您的聚合輸出可以是您迭代處理結果的遊標 –