हालांकि इसे आपके प्रश्न में और स्पष्ट किया जाना चाहिए था, स्रोत से आपका आउटपुट नमूना बताता है कि आप इसकी तलाश कर रहे हैं:
- प्रति "यूआईडी" संदेशों की कुल संख्या
- "से" में मानों की अलग-अलग गणना
- "से" में मानों की अलग-अलग संख्या
- प्रत्येक "यूआईडी" के लिए प्रति "घंटे" की गणना का सारांश
यह सब एक एकल एकत्रीकरण कथन में संभव है, और यह केवल विशिष्ट सूचियों का कुछ सावधानीपूर्वक प्रबंधन करता है और फिर 24 घंटे की अवधि में प्रत्येक घंटे के परिणामों को मैप करने के लिए कुछ हेरफेर करता है।
यहां सबसे अच्छा तरीका मोंगोडीबी 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
चरण, उद्देश्य डेटा में मौजूद प्रति घंटे और प्रत्येक "यूआईडी" मान के लिए परिणाम प्राप्त करना है। $hour
का आसान तारीख एग्रीगेशन ऑपरेटर
समूहीकरण कुंजी के भाग के रूप में इस मान को प्राप्त करने में मदद करता है।
$addToSet
संचालन अपने आप में "मिनी-ग्रुप" का एक प्रकार है, और यह "से" और "से" मानों में से प्रत्येक के लिए "विशिष्ट सेट" रखने की अनुमति देता है, जबकि अनिवार्य रूप से अभी भी प्रति घंटे समूहीकृत होता है।
अगला $समूह
अधिक "संगठनात्मक" है, क्योंकि प्रत्येक घंटे के लिए रिकॉर्ड की गई "गणना" को एक सरणी में रखा जाता है, जबकि सभी डेटा को "यूआईडी" के अनुसार समूहीकृत किया जाता है। यह मूल रूप से आपको परिणाम के लिए वास्तव में आवश्यक सभी "डेटा" देता है, लेकिन निश्चित रूप से $addToSet
यहां संचालन प्रति घंटे निर्धारित विशिष्ट सेटों के "सरणी के भीतर सरणियाँ" जोड़ रहे हैं।
इन मानों को प्रत्येक "यूआईडी" और केवल प्रति वास्तव में अलग सूचियों के रूप में प्राप्त करने के लिए, $अनविंड
और फिर अंत में केवल विशिष्ट "सेट" के रूप में वापस समूहित करें। वही $addToSet
इसे संकुचित करता है, और $first
संचालन अन्य क्षेत्रों के "पहले" मान लेते हैं, जो "प्रति यूआईडी" डेटा लक्ष्य के लिए पहले से ही समान हैं। हम उनसे खुश हैं, इसलिए उन्हें वैसे ही रखें जैसे वे हैं।
यहां अंतिम चरण अनिवार्य रूप से प्रकृति में "कॉस्मेटिक" हैं और क्लाइंट साइड कोड में समान रूप से प्राप्त किए जा सकते हैं। चूंकि प्रत्येक घंटे के अंतराल के लिए डेटा मौजूद नहीं है, इसलिए इसे प्रत्येक घंटे का प्रतिनिधित्व करने वाले मानों की एक सरणी में मैप करने की आवश्यकता है। संस्करणों के बीच उपलब्ध ऑपरेटरों की क्षमताओं पर यहां दो दृष्टिकोण भिन्न हैं।
MongoDB 3.2 रिलीज़ में, $filter
और $arrayElemAt
ऑपरेटर जो प्रभावी रूप से आपको सभी संभावित इंडेक्स पोजीशन (24 घंटे) के इनपुट स्रोत को "ट्रांसपोज़" करने के लिए लॉजिक बनाने की अनुमति देते हैं, जो उपलब्ध डेटा में उन घंटों की गणना के लिए पहले से ही निर्धारित हैं। यह मूल रूप से प्रत्येक उपलब्ध घंटे के लिए पहले से रिकॉर्ड किए गए मानों का "प्रत्यक्ष लुकअप" है, यह देखने के लिए कि क्या यह मौजूद है, जहां यह गिनती पूर्ण सरणी में स्थानांतरित हो जाती है। जहां यह मौजूद नहीं है, 0
. का डिफ़ॉल्ट मान जगह में प्रयोग किया जाता है।
उन ऑपरेटरों के बिना, यह "मिलान अप" करना अनिवार्य रूप से तुलना करने और स्थानांतरित करने के लिए दोनों सरणियों (रिकॉर्ड किए गए डेटा और पूर्ण 24 पदों) को डी-सामान्य करना है। यह दूसरे दृष्टिकोण में "सूचकांक" मूल्यों की एक साधारण तुलना के साथ हो रहा है यह देखने के लिए कि क्या उस घंटे के लिए कोई परिणाम था। $max
यहां ऑपरेटर मुख्य रूप से दो $अनविंड
. के कारण उपयोग किया जाता है बयान, जहां स्रोत डेटा से प्रत्येक दर्ज मूल्य हर संभावित सूचकांक स्थिति के लिए पुन:पेश किया जा रहा है। यह केवल "सूचकांक घंटे" के लिए वांछित मानों को "संकुचित" करता है।
उस बाद के दृष्टिकोण में यह तब महत्वपूर्ण हो जाता है $sort
कोड>
समूहीकरण पर _id
मूल्य। ऐसा इसलिए है क्योंकि इसमें "इंडेक्स" स्थिति है, और इस सामग्री को एक सरणी में वापस ले जाने पर इसकी आवश्यकता होने वाली है जिसे आप ऑर्डर करने की उम्मीद करते हैं। जो निश्चित रूप से अंतिम $group
है यहां चरण जहां आदेशित पदों को $push
. के साथ एक सरणी में रखा जाता है ।
"विशिष्ट सूचियों" पर वापस जाएं, $size
कोड>
ऑपरेटर का उपयोग सभी मामलों में "लंबाई" निर्धारित करने के लिए किया जाता है और इसलिए "से" और "से" के लिए सूचियों में अलग-अलग मानों की "गिनती" होती है। कम से कम MongoDB 2.6 पर यह एकमात्र वास्तविक बाधा है, लेकिन अन्यथा इसे प्रत्येक सरणी को व्यक्तिगत रूप से "अनइंडिंग" के साथ प्रतिस्थापित किया जा सकता है और फिर _id
पर वापस समूहित किया जा सकता है प्रत्येक सेट में सरणी प्रविष्टियों को गिनने के लिए पहले से मौजूद है। यह एक बुनियादी प्रक्रिया है, लेकिन जैसा कि आपको $size
. देखना चाहिए समग्र प्रदर्शन के लिए यहां ऑपरेटर बेहतर विकल्प है।
अंतिम नोट के रूप में, आपका निष्कर्ष डेटा थोड़ा हटकर है, क्योंकि संभवतः "से" में "ddd" के साथ प्रविष्टि का उद्देश्य "to" में भी समान होना था, लेकिन इसके बजाय "bbb" के रूप में दर्ज किया गया है। यह एक प्रविष्टि द्वारा "से" नीचे के लिए तीसरे "यूआईडी" समूह की विशिष्ट गणना को बदल देता है। लेकिन निश्चित रूप से स्रोत डेटा को देखते हुए तार्किक परिणाम अच्छे हैं:
{ "_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 स्रोत में एक टाइपो भी है जिसमें सीमांकक :
. के साथ इंटरपोज किया जा रहा है सभी लाइनों पर टाइमस्टैम्प के ठीक बाद अल्पविराम के बजाय।