तो आपके पास जो प्रश्न है वह वास्तव में "दस्तावेज़" का चयन करता है जैसे इसे करना चाहिए। लेकिन आप जो खोज रहे हैं वह "सरणी को फ़िल्टर करना" है ताकि लौटाए गए तत्व केवल क्वेरी की स्थिति से मेल खाते हों।
वास्तविक उत्तर निश्चित रूप से यह है कि जब तक आप वास्तव में इस तरह के विवरण को फ़िल्टर करके बहुत अधिक बैंडविड्थ नहीं बचा रहे हैं, तब तक आपको कोशिश भी नहीं करनी चाहिए, या कम से कम पहले स्थितीय मैच से आगे नहीं बढ़ना चाहिए।
MongoDB में स्थितीय $
है ऑपरेटर जो एक क्वेरी स्थिति से मिलान किए गए इंडेक्स पर एक सरणी तत्व लौटाएगा। हालांकि, यह केवल "बाहरी" सबसे सरणी तत्व का "पहला" मिलान सूचकांक देता है।
db.getCollection('retailers').find(
{ 'stores.offers.size': 'L'},
{ 'stores.$': 1 }
)
इस मामले में, इसका अर्थ है "stores"
केवल सरणी स्थिति। इसलिए यदि कई "स्टोर" प्रविष्टियां थीं, तो आपकी मिलान वाली स्थिति वाले तत्वों में से केवल "एक" लौटाया जाएगा। लेकिन , जो "offers"
. की आंतरिक सरणी के लिए कुछ नहीं करता है , और इस तरह मिलान किए गए "stores"
. के भीतर हर "ऑफ़र" सरणी अभी भी वापस आ जाएगी।
MongoDB के पास मानक क्वेरी में इसे "फ़िल्टर" करने का कोई तरीका नहीं है, इसलिए निम्नलिखित काम नहीं करता है:
db.getCollection('retailers').find(
{ 'stores.offers.size': 'L'},
{ 'stores.$.offers.$': 1 }
)
एकमात्र उपकरण MongoDB को वास्तव में इस स्तर के हेरफेर को एकत्रीकरण ढांचे के साथ करना है। लेकिन विश्लेषण आपको दिखाएगा कि आपको "शायद" ऐसा क्यों नहीं करना चाहिए, और इसके बजाय केवल कोड में सरणी को फ़िल्टर करें।
आप इसे प्रति संस्करण कैसे प्राप्त कर सकते हैं इसके क्रम में।
सबसे पहले MongoDB 3.2.x . के साथ $filter
. का उपयोग करके ऑपरेशन:
db.getCollection('retailers').aggregate([
{ "$match": { "stores.offers.size": "L" } },
{ "$project": {
"stores": {
"$filter": {
"input": {
"$map": {
"input": "$stores",
"as": "store",
"in": {
"_id": "$$store._id",
"offers": {
"$filter": {
"input": "$$store.offers",
"as": "offer",
"cond": {
"$setIsSubset": [ ["L"], "$$offer.size" ]
}
}
}
}
}
},
"as": "store",
"cond": { "$ne": [ "$$store.offers", [] ]}
}
}
}}
])
फिर MongoDB 2.6.x . के साथ और ऊपर $map
. के साथ और $setDifference
:
db.getCollection('retailers').aggregate([
{ "$match": { "stores.offers.size": "L" } },
{ "$project": {
"stores": {
"$setDifference": [
{ "$map": {
"input": {
"$map": {
"input": "$stores",
"as": "store",
"in": {
"_id": "$$store._id",
"offers": {
"$setDifference": [
{ "$map": {
"input": "$$store.offers",
"as": "offer",
"in": {
"$cond": {
"if": { "$setIsSubset": [ ["L"], "$$offer.size" ] },
"then": "$$offer",
"else": false
}
}
}},
[false]
]
}
}
}
},
"as": "store",
"in": {
"$cond": {
"if": { "$ne": [ "$$store.offers", [] ] },
"then": "$$store",
"else": false
}
}
}},
[false]
]
}
}}
])
और अंत में MongoDB 2.2.x . से ऊपर के किसी भी संस्करण में जहां एकत्रीकरण ढांचा पेश किया गया था।
db.getCollection('retailers').aggregate([
{ "$match": { "stores.offers.size": "L" } },
{ "$unwind": "$stores" },
{ "$unwind": "$stores.offers" },
{ "$match": { "stores.offers.size": "L" } },
{ "$group": {
"_id": {
"_id": "$_id",
"storeId": "$stores._id",
},
"offers": { "$push": "$stores.offers" }
}},
{ "$group": {
"_id": "$_id._id",
"stores": {
"$push": {
"_id": "$_id.storeId",
"offers": "$offers"
}
}
}}
])
आइए स्पष्टीकरणों को तोड़ें।
MongoDB 3.2.x और उच्चतर
तो आम तौर पर बोलते हुए, $filter
यहां जाने का रास्ता है क्योंकि इसे इस उद्देश्य को ध्यान में रखकर बनाया गया है। चूंकि सरणी के कई स्तर हैं, इसलिए आपको इसे प्रत्येक स्तर पर लागू करने की आवश्यकता है। तो सबसे पहले आप प्रत्येक "offers"
. में गोता लगा रहे हैं "stores"
. के भीतर जांच करने के लिए और $filter
वह सामग्री।
यहाँ सरल तुलना है "क्या "size"
सरणी में वह तत्व है जिसकी मुझे तलाश है" . इस तार्किक संदर्भ में, केवल $setIsSubset
. का उपयोग करना है ["L"]
. की एक सरणी ("सेट") की तुलना करने के लिए ऑपरेशन लक्ष्य सरणी के लिए। जहां वह शर्त true
है (इसमें "L" शामिल है) फिर "offers"
. के लिए सरणी तत्व बनाए रखा जाता है और परिणाम में वापस कर दिया जाता है।
उच्च स्तर में $filter
, तो आप यह देखना चाहते हैं कि क्या उस पिछले $filter
. का परिणाम है एक खाली सरणी लौटा दी []
"offers"
. के लिए . यदि यह खाली नहीं है, तो तत्व वापस कर दिया जाता है या अन्यथा इसे हटा दिया जाता है।
MongoDB 2.6.x
यह आधुनिक प्रक्रिया के समान ही है, सिवाय इसके कि कोई $filter
. नहीं है इस संस्करण में आप $map
. का उपयोग कर सकते हैं प्रत्येक तत्व का निरीक्षण करने के लिए और फिर $setDifference
. का उपयोग करें false
. के रूप में लौटाए गए किसी भी तत्व को फ़िल्टर करने के लिए ।
तो $map
पूरी सरणी वापस करने जा रहा है, लेकिन $cond
ऑपरेशन सिर्फ यह तय करता है कि तत्व को वापस करना है या इसके बजाय false
मूल्य। $setDifference
. की तुलना में [false]
. के एकल तत्व "सेट" के लिए सभी false
लौटाए गए सरणी में तत्वों को हटा दिया जाएगा।
अन्य सभी तरीकों से, तर्क ऊपर जैसा ही है।
MongoDB 2.2.x और बाद वाले वर्शन
तो MongoDB 2.6 के नीचे सरणियों के साथ काम करने का एकमात्र उपकरण है $unwind
, और केवल इसी उद्देश्य के लिए आपको नहीं . करना चाहिए इस उद्देश्य के लिए "जस्ट" एकत्रीकरण ढांचे का उपयोग करें।
प्रक्रिया वास्तव में सरल प्रतीत होती है, बस प्रत्येक सरणी को "अलग करके", उन चीज़ों को फ़िल्टर करके जिनकी आपको आवश्यकता नहीं है, फिर इसे वापस एक साथ रखकर। मुख्य देखभाल "दो" $group
. में है चरण, "पहले" के साथ आंतरिक सरणी को फिर से बनाने के लिए, और अगला बाहरी सरणी को फिर से बनाने के लिए। अलग-अलग हैं _id
मूल्य सभी स्तरों पर हैं, इसलिए इन्हें समूहीकरण के प्रत्येक स्तर पर शामिल करने की आवश्यकता है।
लेकिन समस्या यह है कि $unwind
बहुत महंगा है . हालांकि इसका उद्देश्य अभी भी है, इसका मुख्य उपयोग इरादा प्रति दस्तावेज़ इस प्रकार की फ़िल्टरिंग नहीं करना है। वास्तव में आधुनिक रिलीज में इसका उपयोग केवल तभी होना चाहिए जब सरणी के तत्व को "ग्रुपिंग कुंजी" का हिस्सा बनने की आवश्यकता हो।
निष्कर्ष
इसलिए इस तरह की एक सरणी के कई स्तरों पर मिलान प्राप्त करना आसान प्रक्रिया नहीं है, और वास्तव में यह बेहद महंगा हो सकता है अगर गलत तरीके से लागू किया गया है।
इस उद्देश्य के लिए केवल दो आधुनिक लिस्टिंग का उपयोग किया जाना चाहिए, क्योंकि वे "क्वेरी" $match
के अतिरिक्त एक "एकल" पाइपलाइन चरण का उपयोग करती हैं। "फ़िल्टरिंग" करने के लिए। परिणामी प्रभाव .find()
. के मानक रूपों की तुलना में थोड़ा अधिक ओवरहेड है ।
सामान्य तौर पर, हालांकि, उन सूचियों में अभी भी उनके लिए जटिलता की मात्रा है, और वास्तव में जब तक आप वास्तव में इस तरह के फ़िल्टरिंग द्वारा लौटाई गई सामग्री को इस तरह से कम नहीं कर रहे हैं जो सर्वर और क्लाइंट के बीच उपयोग की जाने वाली बैंडविड्थ में महत्वपूर्ण सुधार करता है, तो आप बेहतर हैं प्रारंभिक क्वेरी और मूल प्रक्षेपण के परिणाम को फ़िल्टर करने के लिए।
db.getCollection('retailers').find(
{ 'stores.offers.size': 'L'},
{ 'stores.$': 1 }
).forEach(function(doc) {
// Technically this is only "one" store. So omit the projection
// if you wanted more than "one" match
doc.stores = doc.stores.filter(function(store) {
store.offers = store.offers.filter(function(offer) {
return offer.size.indexOf("L") != -1;
});
return store.offers.length != 0;
});
printjson(doc);
})
तो लौटाई गई वस्तु "पोस्ट" क्वेरी प्रोसेसिंग के साथ काम करना ऐसा करने के लिए एकत्रीकरण पाइपलाइन का उपयोग करने से कहीं कम उलझन में है। और जैसा कि कहा गया है कि केवल "वास्तविक" अंतर यह होगा कि आप "सर्वर" पर अन्य तत्वों को प्राप्त करने पर "प्रति दस्तावेज़" को हटाने के विरोध में त्याग रहे हैं, जो थोड़ा बैंडविड्थ बचा सकता है।
लेकिन जब तक आप इसे केवल . के साथ आधुनिक रिलीज़ में नहीं कर रहे हैं $match
और $project
, तो सर्वर पर प्रसंस्करण की "लागत" पहले बेजोड़ तत्वों को अलग करके उस नेटवर्क ओवरहेड को कम करने के "लाभ" से बहुत अधिक हो जाएगी।
सभी मामलों में, आपको एक ही परिणाम मिलता है:
{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"stores" : [
{
"_id" : ObjectId("56f277b5279871c20b8b4783"),
"offers" : [
{
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"size" : [
"S",
"L",
"XL"
]
}
]
}
]
}