MongoDB के $inक्लॉज का उपयोग करते समय , लौटाए गए दस्तावेजों का क्रम हमेशा सरणी तर्क के क्रम के अनुरूप होता है?
MongoDB के $inक्लॉज का उपयोग करते समय , लौटाए गए दस्तावेजों का क्रम हमेशा सरणी तर्क के क्रम के अनुरूप होता है?
जवाबों:
जैसा कि उल्लेख किया गया है, खंड में एक $ के सरणी में तर्कों का क्रम दस्तावेजों को कैसे पुनर्प्राप्त किया जाता है के आदेश को प्रतिबिंबित नहीं करता है। यह निश्चित रूप से प्राकृतिक क्रम होगा या चयनित सूचकांक क्रम के अनुसार दिखाया जाएगा।
यदि आपको इस आदेश को संरक्षित करने की आवश्यकता है, तो आपके पास मूल रूप से दो विकल्प हैं।
तो मान लीजिए कि आप के मूल्यों पर मिलान रहे थे _idएक सरणी है कि करने के लिए पारित किया जा रहा है के साथ अपने दस्तावेजों में $inके रूप में [ 4, 2, 8 ]।
var list = [ 4, 2, 8 ];
db.collection.aggregate([
// Match the selected documents by "_id"
{ "$match": {
"_id": { "$in": [ 4, 2, 8 ] },
},
// Project a "weight" to each document
{ "$project": {
"weight": { "$cond": [
{ "$eq": [ "$_id", 4 ] },
1,
{ "$cond": [
{ "$eq": [ "$_id", 2 ] },
2,
3
]}
]}
}},
// Sort the results
{ "$sort": { "weight": 1 } }
])
तो वह विस्तारित रूप होगा। मूल रूप से यहां क्या होता है, जैसा कि मूल्यों के सरणी को पारित किया जाता है, वैसे ही मूल्यों का परीक्षण करने और एक उचित वजन प्रदान करने के लिए $in"नेस्टेड" $condबयान का निर्माण करें । जैसा कि "वजन" मान सरणी में तत्वों के क्रम को दर्शाता है, तो आप आवश्यक क्रम में अपने परिणाम प्राप्त करने के लिए उस मूल्य को एक प्रकार के चरण में पारित कर सकते हैं।
बेशक आप कोड में पाइपलाइन स्टेटमेंट को वास्तव में "बिल्ड" करते हैं, इस तरह से:
var list = [ 4, 2, 8 ];
var stack = [];
for (var i = list.length - 1; i > 0; i--) {
var rec = {
"$cond": [
{ "$eq": [ "$_id", list[i-1] ] },
i
]
};
if ( stack.length == 0 ) {
rec["$cond"].push( i+1 );
} else {
var lval = stack.pop();
rec["$cond"].push( lval );
}
stack.push( rec );
}
var pipeline = [
{ "$match": { "_id": { "$in": list } }},
{ "$project": { "weight": stack[0] }},
{ "$sort": { "weight": 1 } }
];
db.collection.aggregate( pipeline );
बेशक अगर यह सब आपकी संवेदनाओं के लिए भारी लगता है, तो आप मैपआरड्यूस का उपयोग करके वही काम कर सकते हैं, जो सरल दिखता है, लेकिन संभवतः थोड़ा धीमा चलेगा।
var list = [ 4, 2, 8 ];
db.collection.mapReduce(
function () {
var order = inputs.indexOf(this._id);
emit( order, { doc: this } );
},
function() {},
{
"out": { "inline": 1 },
"query": { "_id": { "$in": list } },
"scope": { "inputs": list } ,
"finalize": function (key, value) {
return value.doc;
}
}
)
और यह मूल रूप से इनपुट सरणी में कैसे होता है के "इंडेक्स ऑर्डर" में उत्सर्जित "कुंजी" मूल्यों पर निर्भर करता है।
इसलिए वे अनिवार्य रूप से एक इनपुट सूची के क्रम को एक ऐसी $inस्थिति में बनाए रखने के आपके तरीके हैं जहां आपके पास पहले से ही एक निर्धारित क्रम में वह सूची है।
केवल MongoDB verion> = 3.4 के लिए लागू एकत्रीकरण क्वेरी का उपयोग करने का दूसरा तरीका -
इस अच्छी ब्लॉग पोस्ट का श्रेय जाता है ।
इस क्रम में लाए जाने वाले उदाहरण दस्तावेज -
var order = [ "David", "Charlie", "Tess" ];
पूछताछ -
var query = [
{$match: {name: {$in: order}}},
{$addFields: {"__order": {$indexOfArray: [order, "$name" ]}}},
{$sort: {"__order": 1}}
];
var result = db.users.aggregate(query);
उपयोग किए गए इन एकत्रीकरण ऑपरेटरों को समझाते हुए पोस्ट से एक और उद्धरण -
3.4 में "$ addFields" चरण नया है और यह आपको अन्य सभी मौजूदा क्षेत्रों को जाने बिना मौजूदा दस्तावेज़ों के लिए "$ प्रोजेक्ट" नए फ़ील्ड की अनुमति देता है। नया "$ indexOfArray" अभिव्यक्ति किसी दिए गए सरणी में विशेष तत्व की स्थिति देता है।
मूल रूप से addFieldsऑपरेटर orderहर दस्तावेज़ के लिए एक नया फ़ील्ड जोड़ता है जब वह इसे पाता है और यह orderफ़ील्ड हमारे द्वारा प्रदान किए गए हमारे सरणी के मूल क्रम का प्रतिनिधित्व करता है। फिर हम इस क्षेत्र के आधार पर दस्तावेजों को क्रमबद्ध करते हैं।
यदि आप उपयोग नहीं करना चाहते हैं aggregate, तो एक अन्य समाधान का उपयोग करना है findऔर उसके बाद ग्राहक-पक्ष का उपयोग करके डॉक्स परिणाम सॉर्ट करें array#sort:
यदि $inमान आदिम प्रकार हैं जैसे संख्याएँ आप एक दृष्टिकोण का उपयोग कर सकते हैं जैसे:
var ids = [4, 2, 8, 1, 9, 3, 5, 6];
MyModel.find({ _id: { $in: ids } }).exec(function(err, docs) {
docs.sort(function(a, b) {
// Sort docs by the order of their _id values in ids.
return ids.indexOf(a._id) - ids.indexOf(b._id);
});
});
यदि $inमान एस जैसे गैर-आदिम प्रकार हैं ObjectId, तो indexOfउस मामले में संदर्भ द्वारा तुलना के रूप में एक और दृष्टिकोण की आवश्यकता होती है।
यदि आप Node.js 4.x + का उपयोग कर रहे हैं, तो आप फ़ंक्शन को बदलकर Array#findIndexऔर ObjectID#equalsइसे संभालने के लिए उपयोग कर सकते हैं sort:
docs.sort((a, b) => ids.findIndex(id => a._id.equals(id)) -
ids.findIndex(id => b._id.equals(id)));
या किसी भी Node.js संस्करण के साथ, अंडरस्कोर / लॉश के साथ findIndex:
docs.sort(function (a, b) {
return _.findIndex(ids, function (id) { return a._id.equals(id); }) -
_.findIndex(ids, function (id) { return b._id.equals(id); });
});
Document#equalsके _idक्षेत्र की तुलना करने के लिए मोंगोसे का उपयोग कर रहा था । _idतुलना स्पष्ट करने के लिए अद्यतन किया गया । पूछने के लिए धन्यवाद।
करने के लिए इसी तरह के JonnyHK के समाधान, आप दस्तावेज़ से लौटे क्रम बदल सकते हैं findअपने ग्राहक में के संयोजन के साथ (यदि आपके ग्राहक जावास्क्रिप्ट में है) mapऔर Array.prototype.findECMAScript 2015 में समारोह:
Collection.find({ _id: { $in: idArray } }).toArray(function(err, res) {
var orderedResults = idArray.map(function(id) {
return res.find(function(document) {
return document._id.equals(id);
});
});
});
नोटों की एक जोड़ी:
idArrayएक सरणी हैObjectIdmapकॉलबैक में अपने कोड को सरल बनाने के लिए कर सकते हैं ।findके प्रत्येक तत्व के लिए एरे को ट्रैवर्स करता है (बाहरी से map)। यह बहुत ही अयोग्य है, क्योंकि लुकअप टेबल का उपयोग करके ओ (एन) समाधान है।
मुझे पता है कि यह सवाल Mongoose JS फ्रेमवर्क से संबंधित है, लेकिन डुप्लिकेटेड एक सामान्य है, इसलिए मुझे उम्मीद है कि यहां Python (PyMongo) समाधान पोस्ट करना ठीक है।
things = list(db.things.find({'_id': {'$in': id_array}}))
things.sort(key=lambda thing: id_array.index(thing['_id']))
# things are now sorted according to id_array order
मानगो रिटर्न के बाद परिणाम ऑर्डर करने का एक आसान तरीका यह है कि आईडी के साथ एक ऑब्जेक्ट को कुंजी के रूप में बनाया जाए और फिर दिए गए _id पर एक सरणी वापस करने के लिए मैप करें जो सही तरीके से ऑर्डर किया गया है।
async function batchUsers(Users, keys) {
const unorderedUsers = await Users.find({_id: {$in: keys}}).toArray()
let obj = {}
unorderedUsers.forEach(x => obj[x._id]=x)
const ordered = keys.map(key => obj[key])
return ordered
}
मुझे पता है कि यह एक पुराना धागा है, लेकिन अगर आप सरणी में केवल आईडी का मान लौटा रहे हैं, तो आपको इस वाक्यविन्यास का विकल्प चुनना पड़ सकता है। जैसा कि मैं एक mongo ObjectId प्रारूप के साथ मिलान करने के लिए indexOf मान प्राप्त नहीं कर सका।
obj.map = function() {
for(var i = 0; i < inputs.length; i++){
if(this._id.equals(inputs[i])) {
var order = i;
}
}
emit(order, {doc: this});
};
Mongo ObjectId को कैसे कन्वर्ट करें। 'ObjectId ()' रैपर को शामिल किए बिना - केवल मान?
आप $ या खंड के साथ आदेश की गारंटी दे सकते हैं।
इसलिए $or: [ _ids.map(_id => ({_id}))]इसके बजाय उपयोग करें ।
$orवैकल्पिक हल काम नहीं किया है v2.6 के बाद से ।
मानगो से परिणाम प्राप्त होने के बाद यह एक कोड समाधान है। इंडेक्स को स्टोर करने के लिए मैप का उपयोग करना और फिर मानों की अदला-बदली करना।
catDetails := make([]CategoryDetail, 0)
err = sess.DB(mdb).C("category").
Find(bson.M{
"_id": bson.M{"$in": path},
"is_active": 1,
"name": bson.M{"$ne": ""},
"url.path": bson.M{"$exists": true, "$ne": ""},
}).
Select(
bson.M{
"is_active": 1,
"name": 1,
"url.path": 1,
}).All(&catDetails)
if err != nil{
return
}
categoryOrderMap := make(map[int]int)
for index, v := range catDetails {
categoryOrderMap[v.Id] = index
}
counter := 0
for i := 0; counter < len(categoryOrderMap); i++ {
if catId := int(path[i].(float64)); catId > 0 {
fmt.Println("cat", catId)
if swapIndex, exists := categoryOrderMap[catId]; exists {
if counter != swapIndex {
catDetails[swapIndex], catDetails[counter] = catDetails[counter], catDetails[swapIndex]
categoryOrderMap[catId] = counter
categoryOrderMap[catDetails[swapIndex].Id] = swapIndex
}
counter++
}
}
}