इसमें बहुत कुछ है, खासकर यदि आप कुल , लेकिन यह कर सकते हैं सामाप्त करो। मैं लिस्टिंग के बाद के चरणों के बारे में बताऊंगा:
db.collection.aggregate([
// 1. Unwind both arrays
{"$unwind": "$win"},
{"$unwind": "$loss"},
// 2. Cast each field with a type and the array on the end
{"$project":{
"win.player": "$win.player",
"win.type": {"$cond":[1,"win",0]},
"loss.player": "$loss.player",
"loss.type": {"$cond": [1,"loss",0]},
"score": {"$cond":[1,["win", "loss"],0]}
}},
// Unwind the "score" array
{"$unwind": "$score"},
// 3. Reshape to "result" based on the value of "score"
{"$project": {
"result.player": {"$cond": [
{"$eq": ["$win.type","$score"]},
"$win.player",
"$loss.player"
] },
"result.type": {"$cond": [
{"$eq":["$win.type", "$score"]},
"$win.type",
"$loss.type"
]}
}},
// 4. Get all unique result within each document
{"$group": { "_id": { "_id":"$_id", "result": "$result" } }},
// 5. Sum wins and losses across documents
{"$group": {
"_id": "$_id.result.player",
"wins": {"$sum": {"$cond": [
{"$eq":["$_id.result.type","win"]},1,0
]}},
"losses": {"$sum":{"$cond": [
{"$eq":["$_id.result.type","loss"]},1,0
]}}
}}
])
सारांश
यह धारणा लेता है कि प्रत्येक "जीत" और "हानि" सरणी में "खिलाड़ी" शुरू करने के लिए सभी अद्वितीय हैं। यहाँ जो दिखाया गया है, उसके लिए यह उचित लग रहा था:
-
दोनों सरणियों को खोल दें। यह डुप्लीकेट बनाता है लेकिन बाद में उन्हें हटा दिया जाएगा।
-
प्रोजेक्ट करते समय $cond का कुछ उपयोग होता है कुछ शाब्दिक स्ट्रिंग मान प्राप्त करने के लिए ऑपरेटर (एक टर्नरी)। और अंतिम उपयोग विशेष है, क्योंकि और सरणी जोड़ा जा रहा है। तो उस सरणी को प्रक्षेपित करने के बाद फिर से खाली होने जा रहा है। अधिक डुप्लिकेट, लेकिन यह बात है। प्रत्येक के लिए एक "जीत", एक "हार" रिकॉर्ड।
-
$cond के साथ अधिक प्रक्षेपण ऑपरेटर और $eq का उपयोग ऑपरेटर भी। इस बार हम विलय कर रहे हैं दो फ़ील्ड एक में। तो इसका उपयोग करते हुए, जब फ़ील्ड का "प्रकार" "स्कोर" में मान से मेल खाता है तो उस "कुंजी फ़ील्ड" का उपयोग "परिणाम" फ़ील्ड मान के लिए किया जाता है। परिणाम दो अलग-अलग "जीत" और "हानि" फ़ील्ड अब एक ही नाम साझा करते हैं, जिसे "प्रकार" द्वारा पहचाना जाता है।
-
प्रत्येक दस्तावेज़ के भीतर डुप्लिकेट से छुटकारा पाना। बस दस्तावेज़
_id
. द्वारा समूहीकृत करना और "परिणाम" फ़ील्ड कुंजी के रूप में। अब वही "जीत" और "नुकसान" रिकॉर्ड होना चाहिए जैसा कि मूल दस्तावेज़ में था, बस एक अलग रूप में जैसा कि उन्हें सरणियों से हटा दिया जाता है। -
अंत में "खिलाड़ी" प्रति कुल योग प्राप्त करने के लिए सभी दस्तावेज़ों में समूह बनाएं। $cond का अधिक उपयोग और $eq लेकिन इस बार यह निर्धारित करने के लिए कि क्या वर्तमान दस्तावेज़ "जीत" या "हार" है। तो जहां यह मेल खाता है हम 1 लौटाते हैं और जहां असत्य हम 0 लौटाते हैं। वे मान $योग "जीत" और "हार" की कुल संख्या प्राप्त करने के लिए।
और यह बताता है कि परिणाम कैसे प्राप्त करें।
एकत्रीकरण ऑपरेटरों के बारे में अधिक जानें दस्तावेज़ीकरण से। $cond के लिए कुछ "मजेदार" उपयोग उस लिस्टिंग में एक $ से बदला जा सकता है। शाब्दिक ऑपरेटर। लेकिन यह तब तक उपलब्ध नहीं होगा जब तक कि संस्करण 2.6 और इसके बाद के संस्करण जारी नहीं हो जाते।
MongoDB 2.6 और ऊपर के लिए "सरलीकृत" केस
बेशक एक नया सेट ऑपरेटर है। लेखन के समय आगामी रिलीज़ क्या है, जो इसे कुछ हद तक सरल बनाने में मदद करेगी:
db.collection.aggregate([
{ "$unwind": "$win" },
{ "$project": {
"win.player": "$win.player",
"win.type": { "$literal": "win" },
"loss": 1,
}},
{ "$group": {
"_id" : {
"_id": "$_id",
"loss": "$loss"
},
"win": { "$push": "$win" }
}},
{ "$unwind": "$_id.loss" },
{ "$project": {
"loss.player": "$_id.loss.player",
"loss.type": { "$literal": "loss" },
"win": 1,
}},
{ "$group": {
"_id" : {
"_id": "$_id._id",
"win": "$win"
},
"loss": { "$push": "$loss" }
}},
{ "$project": {
"_id": "$_id._id",
"results": { "$setUnion": [ "$_id.win", "$loss" ] }
}},
{ "$unwind": "$results" },
{ "$group": {
"_id": "$results.player",
"wins": {"$sum": {"$cond": [
{"$eq":["$results.type","win"]},1,0
]}},
"losses": {"$sum":{"$cond": [
{"$eq":["$results.type","loss"]},1,0
]}}
}}
])
लेकिन "सरलीकृत" बहस का विषय है। मेरे लिए, यह सिर्फ "महसूस करता है" जैसे यह "चारों ओर घूम रहा है" और अधिक काम कर रहा है। यह निश्चित रूप से अधिक पारंपरिक है, क्योंकि यह केवल $ पर निर्भर करता है। सेटयूनियन विलय करने के लिए सरणी परिणाम।
लेकिन वह "काम" आपकी स्कीमा को थोड़ा बदलकर रद्द कर दिया जाएगा, जैसा कि यहां दिखाया गया है:
{
"_id" : ObjectId("531ea2b1fcc997d5cc5cbbc9"),
"win": [
{
"player" : "Player2",
"type" : "win"
},
{
"player" : "Player4",
"type" : "win"
}
],
"loss" : [
{
"player" : "Player6",
"type" : "loss"
},
{
"player" : "Player5",
"type" : "loss"
},
]
}
और यह "प्रकार" विशेषता जोड़कर सरणी सामग्री को प्रोजेक्ट करने की आवश्यकता को हटा देता है जैसा कि हम कर रहे हैं, और क्वेरी और किए गए कार्य को कम करता है:
db.collection.aggregate([
{ "$project": {
"results": { "$setUnion": [ "$win", "$loss" ] }
}},
{ "$unwind": "$results" },
{ "$group": {
"_id": "$results.player",
"wins": {"$sum": {"$cond": [
{"$eq":["$results.type","win"]},1,0
]}},
"losses": {"$sum":{"$cond": [
{"$eq":["$results.type","loss"]},1,0
]}}
}}
])
और निश्चित रूप से बस अपना स्कीमा इस प्रकार बदल रहा है:
{
"_id" : ObjectId("531ea2b1fcc997d5cc5cbbc9"),
"results" : [
{
"player" : "Player6",
"type" : "loss"
},
{
"player" : "Player5",
"type" : "loss"
},
{
"player" : "Player2",
"type" : "win"
},
{
"player" : "Player4",
"type" : "win"
}
]
}
यह चीजों को बहुत बनाता है आसान। और यह 2.6 से पहले के संस्करणों में किया जा सकता था। तो आप इसे अभी कर सकते हैं:
db.collection.aggregate([
{ "$unwind": "$results" },
{ "$group": {
"_id": "$results.player",
"wins": {"$sum": {"$cond": [
{"$eq":["$results.type","win"]},1,0
]}},
"losses": {"$sum":{"$cond": [
{"$eq":["$results.type","loss"]},1,0
]}}
}}
])
तो मेरे लिए, अगर यह मेरा आवेदन था, तो मैं आपके पास जो कुछ भी है उसके बजाय ऊपर दिखाए गए अंतिम फॉर्म में स्कीमा चाहता हूं। आपूर्ति किए गए एकत्रीकरण कार्यों में किए गए सभी कार्य (अंतिम विवरण के अपवाद के साथ) का उद्देश्य मौजूदा स्कीमा फॉर्म को लेना और इसे इस में हेरफेर करना है। फॉर्म, इसलिए ऊपर दिखाए गए अनुसार सरल एकत्रीकरण कथन को चलाना आसान है।
चूंकि प्रत्येक खिलाड़ी को "जीत/हार" विशेषता के साथ "टैग" किया जाता है, आप किसी भी तरह अपने "विजेता/हारे" को हमेशा विवेकपूर्वक एक्सेस कर सकते हैं।
अंतिम बात के रूप में। आपकी तारीख एक स्ट्रिंग है। मुझे यह पसंद नहीं है।
ऐसा करने का कोई कारण हो सकता है लेकिन मुझे यह दिखाई नहीं देता। यदि आपको दिन . के अनुसार समूह बनाना है यह केवल उचित बीएसओएन तिथि का उपयोग करके एकत्रीकरण में करना आसान है। फिर आप अन्य समय अंतरालों के साथ भी आसानी से काम कर पाएंगे।
इसलिए यदि आपने तिथि तय कर दी है, और इसे start_date बना दिया है , और "अवधि" को end_time . से बदल दिया , तो आपको कुछ ऐसा रखने को मिलता है जिसे आप साधारण गणित से "अवधि" प्राप्त कर सकते हैं + आपको बहुत सारे अतिरिक्त मिलते हैं इसके बजाय इन्हें दिनांक मान के रूप में रखने से लाभ होता है।
ताकि आपको अपने स्कीमा पर विचार करने के लिए कुछ भोजन मिल सके।
रुचि रखने वालों के लिए, यहां कुछ कोड दिया गया है जिसका उपयोग मैंने डेटा का एक कार्यशील सेट तैयार करने के लिए किया था:
// Ye-olde array shuffle
function shuffle(array) {
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
for ( var l=0; l<10000; l++ ) {
var players = ["Player1","Player2","Player3","Player4"];
var playlist = shuffle(players);
for ( var x=0; x<playlist.length; x++ ) {
var obj = {
player: playlist[x],
score: Math.floor(Math.random() * (100000 - 50 + 1)) +50
};
playlist[x] = obj;
}
var rec = {
duration: Math.floor(Math.random() * (50000 - 15000 +1)) +15000,
date: new Date(),
win: playlist.slice(0,2),
loss: playlist.slice(2)
};
db.game.insert(rec);
}