雖然它應該已經取得了你的問題更清楚,從源的輸出樣本表明,你正在尋找:
- 每「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介於與:
而不是逗號所有線路上的時間戳之後一個錯字。
這背後的邏輯是什麼? 。請使用您的問題上的[編輯](http://stackoverflow.com/posts/35161734/edit)鏈接來解釋您正在嘗試做什麼。您應該閱讀[我如何問一個好問題?](http://stackoverflow.com/help/how-to-ask) – styvane
儘管可以通過查看結果數據來確定邏輯,但它通常是最好的當在這裏提問時「解釋」你的邏輯,以便清楚地理解,正如@ user3100115所建議的那樣。不是一個簡單的過程,但的確有可能。 –