यहाँ प्रश्न वास्तव में कुछ अलग के बारे में है और इसके लिए $lookup
. की आवश्यकता नहीं है बिल्कुल भी। लेकिन "$lookup के बाद फ़िल्टरिंग" के शीर्षक से पूरी तरह से यहां आने वाले किसी के लिए तो ये आपके लिए तकनीकें हैं:
MongoDB 3.6 - सब-पाइपलाइन
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"let": { "id": "$id" },
"pipeline": [
{ "$match": {
"value": "1",
"$expr": { "$in": [ "$$id", "$contain" ] }
}}
],
"as": "childs"
}}
])
पहले - $लुकअप + $अनविंड + $मिलान सहसंयोजन
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$unwind": "$childs" },
{ "$match": { "childs.value": "1" } },
{ "$group": {
"_id": "$_id",
"id": { "$first": "$id" },
"value": { "$first": "$value" },
"contain": { "$first": "$contain" },
"childs": { "$push": "$childs" }
}}
])
अगर आप सवाल करते हैं कि आप $unwind
क्यों करेंगे? $filter
. का उपयोग करने के विपरीत सरणी पर, फिर समग्र $lookup पढ़ें मेलिंग पाइपलाइन में दस्तावेज़ों का कुल आकार अधिकतम दस्तावेज़ आकार से अधिक है, इस पर सभी विवरणों के लिए कि यह आम तौर पर आवश्यक और कहीं अधिक इष्टतम क्यों है।
MongoDB 3.6 और उसके बाद के रिलीज़ के लिए, अधिक अभिव्यंजक "सब-पाइपलाइन" आम तौर पर वह है जो आप विदेशी संग्रह के परिणामों को "फ़िल्टर" करना चाहते हैं, इससे पहले कि कुछ भी सरणी में वापस आ जाए।
उत्तर पर वापस जाएं, हालांकि जो वास्तव में वर्णन करता है कि पूछे गए प्रश्न को "नो जॉइन" की आवश्यकता क्यों है....
मूल
$lookup
. का उपयोग करना जैसे आप यहां जो चाहते हैं उसे करने का यह सबसे "कुशल" तरीका नहीं है। लेकिन इस पर और बाद में।
एक बुनियादी अवधारणा के रूप में, बस $filter
. का उपयोग करें परिणामी सरणी पर:
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$project": {
"id": 1,
"value": 1,
"contain": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$eq": [ "$$child.value", "1" ] }
}
}
}}
]);
या $redact
. का उपयोग करें इसके बजाय:
db.test.aggregate([
{ "$match": { "id": 100 } },
{ "$lookup": {
"from": "test",
"localField": "id",
"foreignField": "contain",
"as": "childs"
}},
{ "$redact": {
"$cond": {
"if": {
"$or": [
{ "$eq": [ "$value", "0" ] },
{ "$eq": [ "$value", "1" ] }
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
]);
दोनों का एक ही परिणाम मिलता है:
{
"_id":ObjectId("570557d4094a4514fc1291d6"),
"id":100,
"value":"0",
"contain":[ ],
"childs":[ {
"_id":ObjectId("570557d4094a4514fc1291d7"),
"id":110,
"value":"1",
"contain":[ 100 ]
},
{
"_id":ObjectId("570557d4094a4514fc1291d8"),
"id":120,
"value":"1",
"contain":[ 100 ]
}
]
}
लब्बोलुआब यह है कि $lookup
केवल कुछ डेटा का चयन करने के लिए स्वयं "अभी तक" क्वेरी नहीं कर सकता है। तो सभी "फ़िल्टरिंग" $lookup
. के बाद होने चाहिए
लेकिन वास्तव में इस प्रकार के "सेल्फ जॉइन" के लिए बेहतर होगा कि आप $lookup
. का उपयोग न करें बिल्कुल और एक अतिरिक्त पढ़ने और "हैश-मर्ज" के ऊपरी हिस्से से पूरी तरह से परहेज करना। बस संबंधित आइटम और $group
लाएं इसके बजाय:
db.test.aggregate([
{ "$match": {
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}},
{ "$group": {
"_id": {
"$cond": {
"if": { "$eq": [ "$value", "0" ] },
"then": "$id",
"else": { "$arrayElemAt": [ "$contain", 0 ] }
}
},
"value": { "$first": { "$literal": "0"} },
"childs": {
"$push": {
"$cond": {
"if": { "$ne": [ "$value", "0" ] },
"then": "$$ROOT",
"else": null
}
}
}
}},
{ "$project": {
"value": 1,
"childs": {
"$filter": {
"input": "$childs",
"as": "child",
"cond": { "$ne": [ "$$child", null ] }
}
}
}}
])
जो केवल थोड़ा अलग निकलता है क्योंकि मैंने जानबूझकर बाहरी क्षेत्रों को हटा दिया है। यदि आप वास्तव में चाहते हैं तो उन्हें अपने आप में जोड़ें:
{
"_id" : 100,
"value" : "0",
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [ 100 ]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [ 100 ]
}
]
}
तो यहाँ एकमात्र वास्तविक मुद्दा किसी भी null
. को "फ़िल्टर" करना है सरणी से परिणाम, तब बनाया गया जब वर्तमान दस्तावेज़ parent
. था आइटम को $push
. पर संसाधित करने में ।
आप यहां जो कुछ भी याद कर रहे हैं वह यह है कि आप जिस परिणाम की तलाश कर रहे हैं उसे एकत्रीकरण या "उप-प्रश्नों" की बिल्कुल भी आवश्यकता नहीं है। जिस संरचना का आपने निष्कर्ष निकाला है या संभवतः कहीं और पाया है वह "डिज़ाइन" है ताकि आप एक ही क्वेरी अनुरोध में "नोड" और उसके सभी "बच्चों" प्राप्त कर सकें।
इसका मतलब है कि केवल "क्वेरी" ही वह सब कुछ है जिसकी वास्तव में आवश्यकता है, और डेटा संग्रह (जो कि हो रहा है क्योंकि कोई सामग्री वास्तव में "कम" नहीं हो रही है) कर्सर परिणाम को पुनरावृत्त करने का एक कार्य है:
var result = {};
db.test.find({
"$or": [
{ "id": 100 },
{ "contain.0": 100, "value": "1" }
]
}).sort({ "contain.0": 1 }).forEach(function(doc) {
if ( doc.id == 100 ) {
result = doc;
result.childs = []
} else {
result.childs.push(doc)
}
})
printjson(result);
यह बिल्कुल वही काम करता है:
{
"_id" : ObjectId("570557d4094a4514fc1291d6"),
"id" : 100,
"value" : "0",
"contain" : [ ],
"childs" : [
{
"_id" : ObjectId("570557d4094a4514fc1291d7"),
"id" : 110,
"value" : "1",
"contain" : [
100
]
},
{
"_id" : ObjectId("570557d4094a4514fc1291d8"),
"id" : 120,
"value" : "1",
"contain" : [
100
]
}
]
}
और इस बात के प्रमाण के रूप में कार्य करता है कि आपको वास्तव में यहां माता-पिता और बच्चों दोनों का चयन करने के लिए "एकल" क्वेरी जारी करने की आवश्यकता है। लौटाया गया डेटा वही है, और आप सर्वर या क्लाइंट पर जो कुछ भी कर रहे हैं वह दूसरे एकत्रित प्रारूप में "मालिश" कर रहा है।
यह उन मामलों में से एक है जहां आप "रिलेशनल" डेटाबेस में चीजों को कैसे करते हैं, इस बारे में सोचकर "पकड़े गए" हो सकते हैं, और यह महसूस नहीं करते कि जिस तरह से डेटा संग्रहीत किया जाता है वह "बदल गया" है, अब आपको उपयोग करने की आवश्यकता नहीं है एक ही दृष्टिकोण।
दस्तावेज़ीकरण उदाहरण का ठीक यही बिंदु है "बाल संदर्भों के साथ मॉडल वृक्ष संरचनाएं" इसकी संरचना में, जहां यह एक प्रश्न के भीतर माता-पिता और बच्चों का चयन करना आसान बनाता है।