2016-02-02 26 views
-2

我有一個數據集,看起來像這樣:MongoDB的彙總查詢,或者太複雜?

{ uid: 1000000, from: "aaa", to: "bbb": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 1000000, from: "aaa", to: "bbb": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 1000000, from: "bbb", to: "ccc": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 1000000, from: "bbb", to: "ccc": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 2000000, from: "aaa", to: "bbb": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 2000000, from: "aaa", to: "bbb": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 2000000, from: "aaa", to: "bbb": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 2000000, from: "aaa", to: "bbb": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 3000000, from: "aaa", to: "aaa": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 3000000, from: "bbb", to: "bbb": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 3000000, from: "ccc", to: "ccc": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 3000000, from: "ddd", to: "bbb": timestamp: ISODate("2016-02-02T18:42:06.336Z") }, 
{ uid: 3000000, from: "eee", to: "eee": timestamp: ISODate("2016-02-02T18:42:06.336Z") } 

是否有可能做一個蒙戈查詢以獲得所需的輸出,還是我必須運行多個獨立的嗎?小時是文檔的一小時。

我也應該注意到,收集每天有大概約1萬個條目。有大約400個不同的uid。

{ _id: 1000000, count: 4, from_count: 2, to_count: 2, hours: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0 ] }, 
{ _id: 2000000, count: 4, from_count: 1, to_count: 1, hours: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0 ] }, 
{ _id: 3000000, count: 5, from_count: 5, to_count: 5, hours: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0 ] }, 
+2

這背後的邏輯是什麼? 。請使用您的問題上的[編輯](http://stackoverflow.com/posts/35161734/edit)鏈接來解釋您正在嘗試做什麼。您應該閱讀[我如何問一個好問題?](http://stackoverflow.com/help/how-to-ask) – styvane

+0

儘管可以通過查看結果數據來確定邏輯,但它通常是最好的當在這裏提問時「解釋」你的邏輯,以便清楚地理解,正如@ user3100115所建議的那樣。不是一個簡單的過程,但的確有可能。 –

回答

3

雖然它應該已經取得了你的問題更清楚,從源的輸出樣本表明,你正在尋找:

  • 每「UID」
  • 消息的總計數「to」中值的不同計數
  • 「from」的值的不同計數
  • 每個「uid」的每「小時」計數摘要

這是所有可能的單一聚合語句,它只需要對不同列表進行一些仔細的管理,然後進行一些操作以在24小時內爲每個小時映射結果。

這裏最好的辦法是通過在MongoDB中3.2中引入運營商的幫助:

db.collection.aggregate([ 
    // First group by hour within "uid" and keep distinct "to" and "from" 
    { "$group": { 
     "_id": { 
      "uid": "$uid", 
      "time": { "$hour": "$timestamp" } 
     }, 
     "from": { "$addToSet": "$from" }, 
     "to": { "$addToSet": "$to" }, 
     "count": { "$sum": 1 } 
    }}, 

    // Roll-up to "uid" and keep each hour in an array 
    { "$group": { 
     "_id": "$_id.uid", 
     "total": { "$sum": "$count" }, 
     "from": { "$addToSet": "$from" }, 
     "to": { "$addToSet": "$to" }, 
     "temp_hours": { 
      "$push": { 
       "index": "$_id.time", 
       "count": "$count" 
      } 
     } 
    }}, 

    // Getting distinct "to" and "from" requires a double unwind of arrays 
    { "$unwind": "$to" }, 
    { "$unwind": "$to" }, 
    { "$unwind": "$from" }, 
    { "$unwind": "$from" }, 

    // And then adding back to sets for distinct 
    { "$group": { 
     "_id": "$_id", 
     "total": { "$first": "$total" }, 
     "from": { "$addToSet": "$from" }, 
     "to": { "$addToSet": "$to" }, 
     "temp_hours": { "$first": "$temp_hours" } 
    }}, 

    // Map out for each hour and count size of distinct lists 
    { "$project": { 
     "count": "$total", 
     "from_count": { "$size": "$from" }, 
     "to_count": { "$size": "$to" }, 
     "hours": { 
      "$map": { 
       "input": [ 
        00,01,02,03,04,05,06,07,08,09,10,11, 
        12,13,14,15,16,17,18,19,20,21,22,23 
       ], 
       "as": "el", 
       "in": { 
         "$ifNull": [ 
          { "$arrayElemAt": [ 
           { "$map": { 
            "input": { "$filter": { 
            "input": "$temp_hours", 
            "as": "tmp", 
            "cond": { 
             "$eq": [ "$$el", "$$tmp.index" ] 
            } 
            }}, 
           "as": "out", 
           "in": "$$out.count" 
           }}, 
           0 
          ]}, 
          0 
         ] 
       } 
      } 
     } 
    }}, 

    // Optionally sort in "uid" order 
    { "$sort": { "_id": 1 } } 
]) 

之前的MongoDB 3.2,你需要更多地參與了一下,在一天的所有時間數組內容映射:

db.collection.aggregate([ 

    // First group by hour within "uid" and keep distinct "to" and "from" 
    { "$group": { 
     "_id": { 
      "uid": "$uid", 
      "time": { "$hour": "$timestamp" } 
     }, 
     "from": { "$addToSet": "$from" }, 
     "to": { "$addToSet": "$to" }, 
     "count": { "$sum": 1 } 
    }}, 

    // Roll-up to "uid" and keep each hour in an array 
    { "$group": { 
     "_id": "$_id.uid", 
     "total": { "$sum": "$count" }, 
     "from": { "$addToSet": "$from" }, 
     "to": { "$addToSet": "$to" }, 
     "temp_hours": { 
      "$push": { 
       "index": "$_id.time", 
       "count": "$count" 
      } 
     } 
    }}, 

    // Getting distinct "to" and "from" requires a double unwind of arrays 
    { "$unwind": "$to" }, 
    { "$unwind": "$to" }, 
    { "$unwind": "$from" }, 
    { "$unwind": "$from" }, 

    // And then adding back to sets for distinct, also adding the indexes array 
    { "$group": { 
     "_id": "$_id", 
     "total": { "$first": "$total" }, 
     "from": { "$addToSet": "$from" }, 
     "to": { "$addToSet": "$to" }, 
     "temp_hours": { "$first": "$temp_hours" }, 
     "indexes": { "$first": { "$literal": [ 
        00,01,02,03,04,05,06,07,08,09,10,11, 
        12,13,14,15,16,17,18,19,20,21,22,23 
     ] } } 
    }}, 

    // Denormalize both arrays 
    { "$unwind": "$temp_hours" }, 
    { "$unwind": "$indexes" }, 

    // Marry up the index entries and keep either the value or 0 
    // Note you are normalizing the double unwind to distinct index 
    { "$group": { 
     "_id": { 
      "_id": "$_id", 
      "index": "$indexes" 
     }, 
     "total": { "$first": "$total" }, 
     "from": { "$first": "$from" }, 
     "to": { "$first": "$to" }, 
     "count": { 
      "$max": { 
       "$cond": [ 
        { "$eq": [ "$indexes", "$temp_hours.index" ] }, 
        "$temp_hours.count", 
        0 
       ] 
      } 
     } 
    }}, 

    // Sort to keep index order - !!Important!!   
    { "$sort": { "_id": 1 } }, 

    // Put the hours into the array and get sizes for other results 
    { "$group": { 
     "_id": "$_id._id", 
     "count": { "$first": "$total" }, 
     "from_count": { "$first": { "$size": "$from" } }, 
     "to_count": { "$first": { "$size": "$to" } }, 
     "hours": { "$push": "$count" } 
    }}, 

    // Optionally sort in "uid" order 
    { "$sort": { "_id": 1 } } 
]) 

要打破下來,這裏兩種方法遵循相同的基本步驟,對的「小時」爲24小時內的映射的唯一真正的區別存在的。

在第一個彙總$group階段,目標是獲取數據中每小時的結果以及每個「uid」值。的$hour簡單的日期聚集操作後,可以獲得這個值作爲分組鍵的一部分。

$addToSet操作是一種在自己的「小團體」的,這是允許保留「有區別的」爲每個「到」和「從」的價值觀,而每小時實質上仍然分組。

下一個$group更「組織」,因爲每個小時記錄的「計數」都保存在一個數組中,同時將所有數據彙總爲按「uid」分組。這基本上爲您提供了真正需要的所有「數據」,但當然這裏的操作只是在每小時確定的不同集合中添加「數組內的數組」。

爲了得到這些值按每個「UID」真正不同的名單,只,有必要使用解構每$unwind數組,然後終於回來集團剛剛與獨特的「套」。同樣的$addToSet壓縮了這一點,並且$first操作只取其他字段的「第一」值,這些字段對於目標「每uid」數據已經是相同的。我們很高興與那些,所以還是他們,因爲他們是。

這裏的最後一個階段實質上是「美容」,在客戶端代碼中也可以實現。由於每個小時間隔內不存在數據,因此需要將其映射到代表每個小時的值數組中。這兩種方法在版本之間可用操作員的能力上有所不同。

在MongoDB的3.2版本中,有​​和$arrayElemAt運營商能夠有效地允許創建爲「轉置」的所有可能的索引位置的輸入源(24小時)到那些已經確定用於計數的值的邏輯可用數據中的那些小時數。這是basicalyl的一個「直接查找」值已經記錄每個可用小時,以查看它是否存在,在哪裏它的計數轉置到整個陣列。如果不存在,則使用默認值0

沒有這些操作符,這樣做「匹配」本質上意味着對兩個數組(記錄數據和全部24個位置)進行去歸一化,以便進行比較和轉置。這就是第二種方法中發生的情況,只需比較「索引」值,看看是否有結果。這裏的$max運算符主要用於兩個$unwind語句,其中來自源數據的每個記錄值將針對每個可能的索引位置被再現。這「壓縮」到只是每個「索引小時」所需的值。

在後一種方法中,_id的分組值爲$sort。這是因爲它包含「索引」位置,當將此內容移回到您希望訂購的數組中時,將需要這個位置。這當然是最後的$group階段,在這裏將有序位置放入與$push陣列。

回到「不同列表」,$size運算符在所有情況下都用於確定列表中「to」和「from」列表中不同值的「長度」並因此「計數」不同值。這至少是MongoDB 2.6的唯一真正約束條件,但它可以被替換爲簡單地「逐個展開」每個陣列,然後重新分組到已存在的_id,以計算每個集合中的數組條目。這是一個基本的過程,但正如你應該看到的那樣,運營商在整體性能方面是更好的選擇。


最後要注意,你的結論的數據是有點過,因爲可能是有「DDD」項中的「從」之意也相同「到」,而是被記錄爲「 BBB」。這將第三個「uid」分組的「截至」的不同計數改爲一個條目。但當然給出的源數據的邏輯結果是聲音:

{ "_id" : 1000000, "count" : 3, "from_count" : 2, "to_count" : 2, "hours" : [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0 ] } 
{ "_id" : 2000000, "count" : 2, "from_count" : 1, "to_count" : 1, "hours" : [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0 ] } 
{ "_id" : 3000000, "count" : 5, "from_count" : 5, "to_count" : 4, "hours" : [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0 ] } 

N.B源還具有與delimitter介於與:而不是逗號所有線路上的時間戳之後一個錯字。