आधुनिक
MongoDB 3.6 से $lookup
. का उपयोग करके इसके लिए एक "उपन्यास" दृष्टिकोण है "सेल्फ़ जॉइन" को ठीक उसी तरह करने के लिए जैसा कि मूल कर्सर प्रसंस्करण नीचे दिखाया गया है।
चूंकि इस रिलीज में आप एक "pipeline"
. निर्दिष्ट कर सकते हैं $lookup
. के लिए तर्क "जॉइन" के स्रोत के रूप में, इसका अनिवार्य रूप से मतलब है कि आप $match
. का उपयोग कर सकते हैं और $limit
सरणी के लिए प्रविष्टियों को इकट्ठा करने और "सीमित" करने के लिए:
db.messages.aggregate([
{ "$group": { "_id": "$conversation_ID" } },
{ "$lookup": {
"from": "messages",
"let": { "conversation": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$conversation_ID", "$$conversation" ] } }},
{ "$limit": 10 },
{ "$project": { "_id": 1 } }
],
"as": "msgs"
}}
])
आप $lookup
. के बाद वैकल्पिक रूप से अतिरिक्त प्रक्षेपण जोड़ सकते हैं _id
. वाले दस्तावेज़ों के बजाय सरणी आइटम को केवल मान बनाने के लिए कुंजी, लेकिन मूल परिणाम केवल उपरोक्त करने से है।
अभी भी बकाया SERVER-9277 है जो वास्तव में सीधे "पुश करने की सीमा" का अनुरोध करता है, लेकिन $lookup
का उपयोग करके इस तरह अंतरिम में एक व्यवहार्य विकल्प है।
नोट :$slice
. भी है जिसे मूल उत्तर लिखने के बाद पेश किया गया था और मूल सामग्री में "बकाया JIRA मुद्दा" द्वारा उल्लेख किया गया था। जबकि आप छोटे परिणाम सेट के साथ एक ही परिणाम प्राप्त कर सकते हैं, इसमें अभी भी "सब कुछ धक्का" सरणी में शामिल है और फिर बाद में अंतिम सरणी आउटपुट को वांछित लंबाई तक सीमित कर देता है।
तो यह मुख्य अंतर है और यह आमतौर पर $slice
. के लिए व्यावहारिक क्यों नहीं है बड़े परिणामों के लिए। लेकिन निश्चित रूप से उन मामलों में वैकल्पिक रूप से उपयोग किया जा सकता है जहां यह है।
वैकल्पिक उपयोग के बारे में कई क्षेत्रों द्वारा मोंगोडब समूह मूल्यों पर कुछ और विवरण हैं।
मूल
जैसा कि पहले कहा गया है, यह असंभव नहीं है लेकिन निश्चित रूप से एक भयानक समस्या है।
वास्तव में यदि आपकी मुख्य चिंता यह है कि आपके परिणामी सरणियाँ असाधारण रूप से बड़ी होने जा रही हैं, तो आपके लिए सबसे अच्छा तरीका यह है कि आप प्रत्येक विशिष्ट "बातचीत_आईडी" को एक व्यक्तिगत क्वेरी के रूप में सबमिट करें और फिर अपने परिणामों को संयोजित करें। बहुत ही MongoDB 2.6 सिंटैक्स में जिसे आपकी भाषा के कार्यान्वयन के आधार पर कुछ ट्वीकिंग की आवश्यकता हो सकती है:
var results = [];
db.messages.aggregate([
{ "$group": {
"_id": "$conversation_ID"
}}
]).forEach(function(doc) {
db.messages.aggregate([
{ "$match": { "conversation_ID": doc._id } },
{ "$limit": 10 },
{ "$group": {
"_id": "$conversation_ID",
"msgs": { "$push": "$_id" }
}}
]).forEach(function(res) {
results.push( res );
});
});
लेकिन यह सब इस बात पर निर्भर करता है कि आप इससे बचने की कोशिश कर रहे हैं या नहीं। तो असली जवाब पर:
यहां पहला मुद्दा यह है कि किसी सरणी में "धकेलने" वाली वस्तुओं की संख्या को "सीमित" करने के लिए कोई फ़ंक्शन नहीं है। यह निश्चित रूप से कुछ ऐसा है जो हम चाहेंगे, लेकिन कार्यक्षमता वर्तमान में मौजूद नहीं है।
दूसरा मुद्दा यह है कि सभी वस्तुओं को एक सरणी में धकेलने पर भी, आप $slice
का उपयोग नहीं कर सकते , या एकत्रीकरण पाइपलाइन में कोई समान ऑपरेटर। तो एक सरल ऑपरेशन के साथ उत्पादित सरणी से केवल "शीर्ष 10" परिणाम प्राप्त करने का कोई मौजूदा तरीका नहीं है।
लेकिन आप वास्तव में अपनी समूह सीमाओं पर प्रभावी रूप से "टुकड़ा" करने के लिए संचालन का एक सेट तैयार कर सकते हैं। यह काफी हद तक शामिल है, और उदाहरण के लिए यहां मैं "कटा हुआ" सरणी तत्वों को केवल "छः" तक कम कर दूंगा। यहां मुख्य कारण इस प्रक्रिया को प्रदर्शित करना और यह दिखाना है कि बिना सरणियों के विनाशकारी हुए इसे कैसे करना है जिसमें वह कुल शामिल नहीं है जिसे आप "स्लाइस" करना चाहते हैं।
दस्तावेज़ों का एक नमूना दिया गया:
{ "_id" : 1, "conversation_ID" : 123 }
{ "_id" : 2, "conversation_ID" : 123 }
{ "_id" : 3, "conversation_ID" : 123 }
{ "_id" : 4, "conversation_ID" : 123 }
{ "_id" : 5, "conversation_ID" : 123 }
{ "_id" : 6, "conversation_ID" : 123 }
{ "_id" : 7, "conversation_ID" : 123 }
{ "_id" : 8, "conversation_ID" : 123 }
{ "_id" : 9, "conversation_ID" : 123 }
{ "_id" : 10, "conversation_ID" : 123 }
{ "_id" : 11, "conversation_ID" : 123 }
{ "_id" : 12, "conversation_ID" : 456 }
{ "_id" : 13, "conversation_ID" : 456 }
{ "_id" : 14, "conversation_ID" : 456 }
{ "_id" : 15, "conversation_ID" : 456 }
{ "_id" : 16, "conversation_ID" : 456 }
आप वहां देख सकते हैं कि आपकी शर्तों के आधार पर समूहित करते समय आपको एक सरणी दस तत्वों के साथ और दूसरी "पांच" के साथ मिल जाएगी। आप यहां क्या करना चाहते हैं, दोनों को "नष्ट" किए बिना शीर्ष "छह" तक कम कर दें, जो केवल "पांच" तत्वों से मेल खाएगा।
और निम्नलिखित प्रश्न:
db.messages.aggregate([
{ "$group": {
"_id": "$conversation_ID",
"first": { "$first": "$_id" },
"msgs": { "$push": "$_id" },
}},
{ "$unwind": "$msgs" },
{ "$project": {
"msgs": 1,
"first": 1,
"seen": { "$eq": [ "$first", "$msgs" ] }
}},
{ "$sort": { "seen": 1 }},
{ "$group": {
"_id": "$_id",
"msgs": {
"$push": {
"$cond": [ { "$not": "$seen" }, "$msgs", false ]
}
},
"first": { "$first": "$first" },
"second": { "$first": "$msgs" }
}},
{ "$unwind": "$msgs" },
{ "$project": {
"msgs": 1,
"first": 1,
"second": 1,
"seen": { "$eq": [ "$second", "$msgs" ] }
}},
{ "$sort": { "seen": 1 }},
{ "$group": {
"_id": "$_id",
"msgs": {
"$push": {
"$cond": [ { "$not": "$seen" }, "$msgs", false ]
}
},
"first": { "$first": "$first" },
"second": { "$first": "$second" },
"third": { "$first": "$msgs" }
}},
{ "$unwind": "$msgs" },
{ "$project": {
"msgs": 1,
"first": 1,
"second": 1,
"third": 1,
"seen": { "$eq": [ "$third", "$msgs" ] },
}},
{ "$sort": { "seen": 1 }},
{ "$group": {
"_id": "$_id",
"msgs": {
"$push": {
"$cond": [ { "$not": "$seen" }, "$msgs", false ]
}
},
"first": { "$first": "$first" },
"second": { "$first": "$second" },
"third": { "$first": "$third" },
"forth": { "$first": "$msgs" }
}},
{ "$unwind": "$msgs" },
{ "$project": {
"msgs": 1,
"first": 1,
"second": 1,
"third": 1,
"forth": 1,
"seen": { "$eq": [ "$forth", "$msgs" ] }
}},
{ "$sort": { "seen": 1 }},
{ "$group": {
"_id": "$_id",
"msgs": {
"$push": {
"$cond": [ { "$not": "$seen" }, "$msgs", false ]
}
},
"first": { "$first": "$first" },
"second": { "$first": "$second" },
"third": { "$first": "$third" },
"forth": { "$first": "$forth" },
"fifth": { "$first": "$msgs" }
}},
{ "$unwind": "$msgs" },
{ "$project": {
"msgs": 1,
"first": 1,
"second": 1,
"third": 1,
"forth": 1,
"fifth": 1,
"seen": { "$eq": [ "$fifth", "$msgs" ] }
}},
{ "$sort": { "seen": 1 }},
{ "$group": {
"_id": "$_id",
"msgs": {
"$push": {
"$cond": [ { "$not": "$seen" }, "$msgs", false ]
}
},
"first": { "$first": "$first" },
"second": { "$first": "$second" },
"third": { "$first": "$third" },
"forth": { "$first": "$forth" },
"fifth": { "$first": "$fifth" },
"sixth": { "$first": "$msgs" },
}},
{ "$project": {
"first": 1,
"second": 1,
"third": 1,
"forth": 1,
"fifth": 1,
"sixth": 1,
"pos": { "$const": [ 1,2,3,4,5,6 ] }
}},
{ "$unwind": "$pos" },
{ "$group": {
"_id": "$_id",
"msgs": {
"$push": {
"$cond": [
{ "$eq": [ "$pos", 1 ] },
"$first",
{ "$cond": [
{ "$eq": [ "$pos", 2 ] },
"$second",
{ "$cond": [
{ "$eq": [ "$pos", 3 ] },
"$third",
{ "$cond": [
{ "$eq": [ "$pos", 4 ] },
"$forth",
{ "$cond": [
{ "$eq": [ "$pos", 5 ] },
"$fifth",
{ "$cond": [
{ "$eq": [ "$pos", 6 ] },
"$sixth",
false
]}
]}
]}
]}
]}
]
}
}
}},
{ "$unwind": "$msgs" },
{ "$match": { "msgs": { "$ne": false } }},
{ "$group": {
"_id": "$_id",
"msgs": { "$push": "$msgs" }
}}
])
आपको सरणी में शीर्ष परिणाम मिलते हैं, अधिकतम छह प्रविष्टियाँ:
{ "_id" : 123, "msgs" : [ 1, 2, 3, 4, 5, 6 ] }
{ "_id" : 456, "msgs" : [ 12, 13, 14, 15 ] }
जैसा कि आप यहां देख सकते हैं, ढेर सारी मस्ती।
शुरू में समूहबद्ध करने के बाद आप मूल रूप से $first
. को "पॉप" करना चाहते हैं सरणी परिणामों के लिए स्टैक का मान बंद करें। इस प्रक्रिया को थोड़ा सरल बनाने के लिए, हम वास्तव में इसे प्रारंभिक ऑपरेशन में करते हैं। तो प्रक्रिया बन जाती है:
$unwind
सरणी- पहले से देखे गए मानों की तुलना
$eq
के साथ करें समानता मैच $sort
"फ्लोट" करने के लिए परिणामfalse
शीर्ष पर अनदेखी मान (यह अभी भी क्रम बनाए रखता है)$group
फिर से वापस आएं और$first
को "पॉप" करें स्टैक पर अगले सदस्य के रूप में अनदेखी मूल्य। साथ ही यह$cond
. का उपयोग करता है सरणी स्टैक में "देखे गए" मानों कोfalse
. से बदलने के लिए ऑपरेटर मूल्यांकन में मदद करने के लिए।
$cond
. के साथ अंतिम क्रिया क्या यह सुनिश्चित करने के लिए है कि भविष्य की पुनरावृत्तियां सरणी के अंतिम मान को बार-बार नहीं जोड़ रही हैं जहां "स्लाइस" गिनती सरणी सदस्यों से अधिक है।
उस पूरी प्रक्रिया को उतनी ही वस्तुओं के लिए दोहराया जाना चाहिए जितना आप "स्लाइस" करना चाहते हैं। चूंकि हमें प्रारंभिक समूह में "पहला" आइटम पहले ही मिल गया है, इसका अर्थ है n-1
वांछित स्लाइस परिणाम के लिए पुनरावृत्तियों।
अंतिम चरण वास्तव में परिणाम के लिए सब कुछ वापस सरणियों में परिवर्तित करने का एक वैकल्पिक चित्रण है जैसा कि अंत में दिखाया गया है। तो वास्तव में केवल सशर्त रूप से आइटम को धक्का देना या false
उनकी मिलान स्थिति से वापस और अंत में सभी false
. को "फ़िल्टर" करना मान इसलिए अंतिम सरणियों में क्रमशः "छह" और "पांच" सदस्य होते हैं।
तो इसे समायोजित करने के लिए कोई मानक ऑपरेटर नहीं है, और आप केवल 5 या 10 या सरणी में जो भी आइटम पुश को "सीमित" नहीं कर सकते हैं। लेकिन अगर आपको वाकई ऐसा करना है, तो यह आपका सबसे अच्छा तरीका है।
आप संभवतः इसे mapReduce के साथ संपर्क कर सकते हैं और एकत्रीकरण ढांचे को एक साथ छोड़ सकते हैं। मैं जो दृष्टिकोण अपनाऊंगा (उचित सीमा के भीतर) सर्वर पर प्रभावी रूप से इन-मेमोरी हैश-मैप होगा और परिणाम को "सीमित" करने के लिए जावास्क्रिप्ट स्लाइस का उपयोग करते हुए उस पर सरणियों को जमा करेगा:
db.messages.mapReduce(
function () {
if ( !stash.hasOwnProperty(this.conversation_ID) ) {
stash[this.conversation_ID] = [];
}
if ( stash[this.conversation_ID.length < maxLen ) {
stash[this.conversation_ID].push( this._id );
emit( this.conversation_ID, 1 );
}
},
function(key,values) {
return 1; // really just want to keep the keys
},
{
"scope": { "stash": {}, "maxLen": 10 },
"finalize": function(key,value) {
return { "msgs": stash[key] };
},
"out": { "inline": 1 }
}
)
ताकि मूल रूप से उत्सर्जित "कुंजी" से मेल खाने वाली "इन-मेमोरी" ऑब्जेक्ट को एक सरणी के साथ बनाया जाए जो आपके परिणामों से प्राप्त होने वाले अधिकतम आकार से अधिक न हो। इसके अतिरिक्त यह अधिकतम स्टैक मिलने पर आइटम को "उत्सर्जन" करने की भी जहमत नहीं उठाता।
कम करने वाला हिस्सा वास्तव में केवल "कुंजी" और एक मान को कम करने के अलावा कुछ भी नहीं करता है। तो बस अगर हमारे रेड्यूसर को कॉल नहीं किया जाता है, जैसा कि सच होगा यदि एक कुंजी के लिए केवल 1 मान मौजूद है, तो अंतिम कार्य "स्टैश" कुंजी को अंतिम आउटपुट में मैप करने का ख्याल रखता है।
इसकी प्रभावशीलता आउटपुट के आकार पर भिन्न होती है, और जावास्क्रिप्ट मूल्यांकन निश्चित रूप से तेज़ नहीं है, लेकिन संभवतः एक पाइपलाइन में बड़ी सरणियों को संसाधित करने की तुलना में तेज़ है।
वास्तव में "स्लाइस" ऑपरेटर या "$ पुश" और "$ एडटॉसेट" पर "सीमा" रखने के लिए जेआईआरए मुद्दों को वोट दें, जो दोनों आसान होंगे। व्यक्तिगत रूप से उम्मीद है कि $map
. में कम से कम कुछ संशोधन किया जा सकता है प्रसंस्करण करते समय "वर्तमान सूचकांक" मान को उजागर करने के लिए ऑपरेटर। यह प्रभावी रूप से "स्लाइसिंग" और अन्य कार्यों की अनुमति देगा।
वास्तव में आप इसे सभी आवश्यक पुनरावृत्तियों को "उत्पन्न" करने के लिए कोड करना चाहेंगे। अगर यहां उत्तर पर्याप्त प्यार और/या अन्य समय लंबित है जो मेरे पास ट्यूट्स में है, तो मैं यह दिखाने के लिए कुछ कोड जोड़ सकता हूं कि यह कैसे करना है। यह पहले से ही काफी लंबी प्रतिक्रिया है।
पाइपलाइन उत्पन्न करने के लिए कोड:
var key = "$conversation_ID";
var val = "$_id";
var maxLen = 10;
var stack = [];
var pipe = [];
var fproj = { "$project": { "pos": { "$const": [] } } };
for ( var x = 1; x <= maxLen; x++ ) {
fproj["$project"][""+x] = 1;
fproj["$project"]["pos"]["$const"].push( x );
var rec = {
"$cond": [ { "$eq": [ "$pos", x ] }, "$"+x ]
};
if ( stack.length == 0 ) {
rec["$cond"].push( false );
} else {
lval = stack.pop();
rec["$cond"].push( lval );
}
stack.push( rec );
if ( x == 1) {
pipe.push({ "$group": {
"_id": key,
"1": { "$first": val },
"msgs": { "$push": val }
}});
} else {
pipe.push({ "$unwind": "$msgs" });
var proj = {
"$project": {
"msgs": 1
}
};
proj["$project"]["seen"] = { "$eq": [ "$"+(x-1), "$msgs" ] };
var grp = {
"$group": {
"_id": "$_id",
"msgs": {
"$push": {
"$cond": [ { "$not": "$seen" }, "$msgs", false ]
}
}
}
};
for ( n=x; n >= 1; n-- ) {
if ( n != x )
proj["$project"][""+n] = 1;
grp["$group"][""+n] = ( n == x ) ? { "$first": "$msgs" } : { "$first": "$"+n };
}
pipe.push( proj );
pipe.push({ "$sort": { "seen": 1 } });
pipe.push(grp);
}
}
pipe.push(fproj);
pipe.push({ "$unwind": "$pos" });
pipe.push({
"$group": {
"_id": "$_id",
"msgs": { "$push": stack[0] }
}
});
pipe.push({ "$unwind": "$msgs" });
pipe.push({ "$match": { "msgs": { "$ne": false } }});
pipe.push({
"$group": {
"_id": "$_id",
"msgs": { "$push": "$msgs" }
}
});
यह maxLen
. तक के मूल पुनरावृत्त दृष्टिकोण का निर्माण करता है $unwind
. के चरणों के साथ करने के लिए $group
. इसमें अंतर्निहित अंतिम अनुमानों और "नेस्टेड" सशर्त विवरण का विवरण भी शामिल है। अंतिम मूल रूप से इस प्रश्न पर लिया गया दृष्टिकोण है:
क्या MongoDB का $in क्लॉज गारंटी ऑर्डर करता है?