आपको यहां मैच को "प्रोजेक्ट" करने की आवश्यकता है क्योंकि सभी MongoDB क्वेरी एक "दस्तावेज़" की तलाश में है जिसमें "कम से कम एक तत्व" है वह "इससे बड़ा" . है आपने जो शर्त मांगी थी।
इसलिए किसी "सरणी" को फ़िल्टर करना आपके पास मौजूद "क्वेरी" शर्त के समान नहीं है।
एक साधारण "प्रक्षेपण" उस स्थिति में "पहले" मिलान किए गए आइटम को वापस कर देगा। तो शायद यह वह नहीं है जो आप चाहते हैं, लेकिन एक उदाहरण के रूप में:
Order.find({ "articles.quantity": { "$gte": 5 } })
.select({ "articles.$": 1 })
.populate({
"path": "articles.article",
"match": { "price": { "$lte": 500 } }
}).exec(function(err,orders) {
// populated and filtered twice
}
)
वह "तरह का" वही करता है जो आप चाहते हैं, लेकिन समस्या वास्तव में यह होने वाली है कि केवल एक पर ही वापस आएगी "articles"
. के भीतर तत्व सरणी।
इसे ठीक से करने के लिए आपको .aggregate()
. की आवश्यकता होगी सरणी सामग्री को फ़िल्टर करने के लिए। आदर्श रूप से यह MongoDB 3.2 और $filter
. के साथ किया जाता है . लेकिन .populate()
. करने का एक खास तरीका भी है यहाँ:
Order.aggregate(
[
{ "$match": { "artciles.quantity": { "$gte": 5 } } },
{ "$project": {
"orderdate": 1,
"articles": {
"$filter": {
"input": "$articles",
"as": "article",
"cond": {
"$gte": [ "$$article.quantity", 5 ]
}
}
},
"__v": 1
}}
],
function(err,orders) {
Order.populate(
orders.map(function(order) { return new Order(order) }),
{
"path": "articles.article",
"match": { "price": { "$lte": 500 } }
},
function(err,orders) {
// now it's all populated and mongoose documents
}
)
}
)
तो यहाँ क्या होता है सरणी का वास्तविक "फ़िल्टरिंग" .aggregate()
के भीतर होता है बयान, लेकिन निश्चित रूप से इसका परिणाम अब "नेवला दस्तावेज़" नहीं है क्योंकि .aggregate()
का एक पहलू है यह है कि यह दस्तावेज़ संरचना को "बदल" सकता है, और इस कारण से नेवला "अनुमान" है कि यह मामला है और बस एक "सादा वस्तु" देता है।
यह वास्तव में कोई समस्या नहीं है, क्योंकि जब आप $project
. देखते हैं चरण, हम वास्तव में परिभाषित स्कीमा के अनुसार दस्तावेज़ में मौजूद सभी समान फ़ील्ड के लिए पूछ रहे हैं। तो भले ही यह सिर्फ एक "सादा वस्तु" है, फिर भी इसे एक नेवला दस्तावेज़ में "कास्टिंग" करने में कोई समस्या नहीं है।
यह वह जगह है जहां .map()
आता है, क्योंकि यह परिवर्तित "दस्तावेज़ों" की एक सरणी देता है, जो तब अगले चरण के लिए महत्वपूर्ण होता है।
अब आप Model.populate()
. पर कॉल करें जो आगे "आबादी" को "नेवला दस्तावेज़ों की सरणी" पर चला सकता है।
फिर परिणाम वही होता है जो आप चाहते हैं।
MongoDB 3.2.x से पुराने संस्करण
केवल एक चीज जो वास्तव में यहां बदलती है, वह है एकत्रीकरण पाइपलाइन, इसलिए संक्षिप्तता के लिए बस इतना ही शामिल करने की आवश्यकता है।
मोंगोडीबी 2.6 - $map
. के संयोजन से सरणियों को फ़िल्टर कर सकते हैं और $setDifference
. परिणाम एक "सेट" है लेकिन यह कोई समस्या नहीं है जब नेवला एक _id
बनाता है डिफ़ॉल्ट रूप से सभी उप-दस्तावेज़ सरणियों पर फ़ील्ड:
[
{ "$match": { "artciles.quantity": { "$gte": 5 } } },
{ "$project": {
"orderdate": 1,
"articles": {
"$setDiffernce": [
{ "$map": {
"input": "$articles",
"as": "article",
"in": {
"$cond": [
{ "$gte": [ "$$article.price", 5 ] },
"$$article",
false
]
}
}},
[false]
]
},
"__v": 1
}}
],
इससे पुराने संशोधनों में $unwind
. का उपयोग किया जाना चाहिए :
[
{ "$match": { "artciles.quantity": { "$gte": 5 } }},
{ "$unwind": "$articles" },
{ "$match": { "artciles.quantity": { "$gte": 5 } }},
{ "$group": {
"_id": "$_id",
"orderdate": { "$first": "$orderdate" },
"articles": { "$push": "$articles" },
"__v": { "$first": "$__v" }
}}
],
$लुकअप वैकल्पिक
एक और विकल्प इसके बजाय "सर्वर" पर सब कुछ करना है। यह $lookup
. के साथ एक विकल्प है MongoDB 3.2 और उच्चतर का:
Order.aggregate(
[
{ "$match": { "artciles.quantity": { "$gte": 5 } }},
{ "$project": {
"orderdate": 1,
"articles": {
"$filter": {
"input": "$articles",
"as": "article",
"cond": {
"$gte": [ "$$article.quantity", 5 ]
}
}
},
"__v": 1
}},
{ "$unwind": "$articles" },
{ "$lookup": {
"from": "articles",
"localField": "articles.article",
"foreignField": "_id",
"as": "articles.article"
}},
{ "$unwind": "$articles.article" },
{ "$group": {
"_id": "$_id",
"orderdate": { "$first": "$orderdate" },
"articles": { "$push": "$articles" },
"__v": { "$first": "$__v" }
}},
{ "$project": {
"orderdate": 1,
"articles": {
"$filter": {
"input": "$articles",
"as": "article",
"cond": {
"$lte": [ "$$article.article.price", 500 ]
}
}
},
"__v": 1
}}
],
function(err,orders) {
}
)
और हालांकि वे केवल सादे दस्तावेज़ हैं, यह वही परिणाम है जो आपको .populate()
से मिलते। दृष्टिकोण। और निश्चित रूप से आप हमेशा जा सकते हैं और सभी मामलों में दस्तावेजों को फिर से "कास्ट" कर सकते हैं यदि आपको वास्तव में चाहिए।
"सबसे छोटा" पथ
यह वास्तव में मूल कथन पर वापस जाता है जहां आप मूल रूप से केवल "स्वीकार" करते हैं कि "क्वेरी" सरणी सामग्री को "फ़िल्टर" करने के लिए नहीं है। .populate()
खुशी से ऐसा कर सकते हैं क्योंकि यह सिर्फ एक और "क्वेरी" है और सुविधा से "दस्तावेजों" में भर रहा है।
इसलिए यदि आप मूल दस्तावेज़ सरणी में अतिरिक्त सरणी सदस्यों को हटाकर वास्तव में बैंडविड्थ के "बकेटलोड्स" को सहेज नहीं रहे हैं, तो बस .filter()
पोस्ट प्रोसेसिंग कोड में उन्हें बाहर करें:
Order.find({ "articles.quantity": { "$gte": 5 } })
.populate({
"path": "articles.article",
"match": { "price": { "$lte": 500 } }
}).exec(function(err,orders) {
orders = orders.filter(function(order) {
order.articles = order.articles.filter(function(article) {
return (
( article.quantity >= 5 ) &&
( article.article != null )
)
});
return order.aricles.length > 0;
})
// orders has non matching entries removed
}
)