आप वर्तमान में MongoDB के विकास संस्करण का उपयोग कर रहे हैं जिसमें कुछ सुविधाएँ सक्षम हैं जो MongoDB 4.0 के साथ आधिकारिक रिलीज़ के रूप में रिलीज़ होने की उम्मीद है। ध्यान दें कि कुछ विशेषताएं अंतिम रिलीज से पहले परिवर्तन के अधीन हो सकती हैं, इसलिए उत्पादन कोड को आपके द्वारा प्रतिबद्ध होने से पहले इसके बारे में पता होना चाहिए।
$convert यहां क्यों विफल रहता है
शायद इसे समझाने का सबसे अच्छा तरीका है कि आप अपने बदले हुए नमूने को देखें, लेकिन इसे ObjectId
. से बदलें _id
. के लिए मान और सरणियों के तहत उन लोगों के लिए "स्ट्रिंग्स":
{
"_id" : ObjectId("5afe5763419503c46544e272"),
"name" : "cinco",
"children" : [ { "_id" : "5afe5763419503c46544e273" } ]
},
{
"_id" : ObjectId("5afe5763419503c46544e273"),
"name" : "quatro",
"ancestors" : [ { "_id" : "5afe5763419503c46544e272" } ],
"children" : [ { "_id" : "5afe5763419503c46544e277" } ]
},
{
"_id" : ObjectId("5afe5763419503c46544e274"),
"name" : "seis",
"children" : [ { "_id" : "5afe5763419503c46544e277" } ]
},
{
"_id" : ObjectId("5afe5763419503c46544e275"),
"name" : "um",
"children" : [ { "_id" : "5afe5763419503c46544e276" } ]
}
{
"_id" : ObjectId("5afe5763419503c46544e276"),
"name" : "dois",
"ancestors" : [ { "_id" : "5afe5763419503c46544e275" } ],
"children" : [ { "_id" : "5afe5763419503c46544e277" } ]
},
{
"_id" : ObjectId("5afe5763419503c46544e277"),
"name" : "três",
"ancestors" : [
{ "_id" : "5afe5763419503c46544e273" },
{ "_id" : "5afe5763419503c46544e274" },
{ "_id" : "5afe5763419503c46544e276" }
]
},
{
"_id" : ObjectId("5afe5764419503c46544e278"),
"name" : "sete",
"children" : [ { "_id" : "5afe5763419503c46544e272" } ]
}
आप किसके साथ काम करने की कोशिश कर रहे थे इसका एक सामान्य अनुकरण देना चाहिए।
आपने जो प्रयास किया वह _id
. को रूपांतरित करने का था $project
. के माध्यम से "स्ट्रिंग" में मान $graphLookup
. में प्रवेश करने से पहले मंच। इसके विफल होने का कारण यह है कि आपने प्रारंभिक $project
किया था इस पाइपलाइन के "भीतर", समस्या यह है कि $graphLookup
. के लिए स्रोत "from"
. में विकल्प अभी भी अपरिवर्तित संग्रह है और इसलिए आपको बाद के "लुकअप" पुनरावृत्तियों पर सही विवरण नहीं मिलता है।
db.strcoll.aggregate([
{ "$match": { "name": "três" } },
{ "$addFields": {
"_id": { "$toString": "$_id" }
}},
{ "$graphLookup": {
"from": "strcoll",
"startWith": "$ancestors._id",
"connectFromField": "ancestors._id",
"connectToField": "_id",
"as": "ANCESTORS_FROM_BEGINNING"
}},
{ "$project": {
"name": 1,
"ANCESTORS_FROM_BEGINNING": "$ANCESTORS_FROM_BEGINNING._id"
}}
])
इसलिए "लुकअप" से मेल नहीं खाता:
{
"_id" : "5afe5763419503c46544e277",
"name" : "três",
"ANCESTORS_FROM_BEGINNING" : [ ]
}
समस्या को "पैच करना"
हालांकि यह मुख्य समस्या है और $convert
. की विफलता नहीं है या यह उपनाम ही है। इसे वास्तव में काम करने के लिए हम इसके बजाय एक "व्यू" बना सकते हैं जो इनपुट के लिए खुद को एक संग्रह के रूप में प्रस्तुत करता है।
मैं इसे दूसरे तरीके से करूँगा और "स्ट्रिंग्स" को ObjectId
. में बदल दूंगा $toObjectId
. के माध्यम से :
db.createView("idview","strcoll",[
{ "$addFields": {
"ancestors": {
"$ifNull": [
{ "$map": {
"input": "$ancestors",
"in": { "_id": { "$toObjectId": "$$this._id" } }
}},
"$$REMOVE"
]
},
"children": {
"$ifNull": [
{ "$map": {
"input": "$children",
"in": { "_id": { "$toObjectId": "$$this._id" } }
}},
"$$REMOVE"
]
}
}}
])
हालांकि "दृश्य" का उपयोग करने का अर्थ है कि डेटा को लगातार परिवर्तित मूल्यों के साथ देखा जाता है। तो निम्नलिखित एकत्रीकरण दृश्य का उपयोग कर:
db.idview.aggregate([
{ "$match": { "name": "três" } },
{ "$graphLookup": {
"from": "idview",
"startWith": "$ancestors._id",
"connectFromField": "ancestors._id",
"connectToField": "_id",
"as": "ANCESTORS_FROM_BEGINNING"
}},
{ "$project": {
"name": 1,
"ANCESTORS_FROM_BEGINNING": "$ANCESTORS_FROM_BEGINNING._id"
}}
])
अपेक्षित आउटपुट देता है:
{
"_id" : ObjectId("5afe5763419503c46544e277"),
"name" : "três",
"ANCESTORS_FROM_BEGINNING" : [
ObjectId("5afe5763419503c46544e275"),
ObjectId("5afe5763419503c46544e273"),
ObjectId("5afe5763419503c46544e274"),
ObjectId("5afe5763419503c46544e276"),
ObjectId("5afe5763419503c46544e272")
]
}
समस्या को ठीक करना
उस सब के साथ, यहां असली मुद्दा यह है कि आपके पास कुछ डेटा है जो एक ObjectId
जैसा "दिखता है" मान और वास्तव में एक ObjectId
. के रूप में मान्य है , हालांकि इसे "स्ट्रिंग" के रूप में दर्ज किया गया है। काम करने वाली हर चीज के लिए मूल मुद्दा यह है कि दो "प्रकार" समान नहीं हैं और इसके परिणामस्वरूप एक समानता बेमेल होती है क्योंकि "जुड़ने" का प्रयास किया जाता है।
तो असली सुधार अभी भी वैसा ही है जैसा हमेशा से रहा है, जो इसके बजाय डेटा के माध्यम से जाना और इसे ठीक करना है ताकि "स्ट्रिंग्स" वास्तव में भी हो ObjectId
मूल्य। इसके बाद ये _id
. से मेल खाएंगे कुंजियाँ जिन्हें वे संदर्भित करने के लिए हैं, और आप ObjectId
के बाद से काफी मात्रा में संग्रहण स्थान बचा रहे हैं हेक्साडेसिमल वर्णों में इसके स्ट्रिंग प्रतिनिधित्व की तुलना में स्टोर करने के लिए बहुत कम जगह लेता है।
MongoDB 4.0 विधियों का उपयोग करके, आप "कर सकते थे" वास्तव में "$toObjectId"
. का उपयोग करें एक नया संग्रह लिखने के लिए, बस उसी मामले में जिसे हमने पहले "दृश्य" बनाया था:
db.strcoll.aggregate([
{ "$addFields": {
"ancestors": {
"$ifNull": [
{ "$map": {
"input": "$ancestors",
"in": { "_id": { "$toObjectId": "$$this._id" } }
}},
"$$REMOVE"
]
},
"children": {
"$ifNull": [
{ "$map": {
"input": "$children",
"in": { "_id": { "$toObjectId": "$$this._id" } }
}},
"$$REMOVE"
]
}
}}
{ "$out": "fixedcol" }
])
या निश्चित रूप से जहां आपको समान संग्रह रखने के लिए "आवश्यकता" है, तो पारंपरिक "लूप और अपडेट" वही रहता है जो हमेशा आवश्यक होता है:
var updates = [];
db.strcoll.find().forEach(doc => {
var update = { '$set': {} };
if ( doc.hasOwnProperty('children') )
update.$set.children = doc.children.map(e => ({ _id: new ObjectId(e._id) }));
if ( doc.hasOwnProperty('ancestors') )
update.$set.ancestors = doc.ancestors.map(e => ({ _id: new ObjectId(e._id) }));
updates.push({
"updateOne": {
"filter": { "_id": doc._id },
update
}
});
if ( updates.length > 1000 ) {
db.strcoll.bulkWrite(updates);
updates = [];
}
})
if ( updates.length > 0 ) {
db.strcoll.bulkWrite(updates);
updates = [];
}
वास्तव में एक ही बार में पूरे सरणी को ओवरराइट करने के कारण वास्तव में "स्लेजहैमर" का एक सा है। उत्पादन वातावरण के लिए एक अच्छा विचार नहीं है, लेकिन इस अभ्यास के प्रयोजनों के लिए एक प्रदर्शन के रूप में पर्याप्त है।
निष्कर्ष
तो जबकि मोंगोडीबी 4.0 इन "कास्टिंग" सुविधाओं को जोड़ देगा जो वास्तव में बहुत उपयोगी हो सकते हैं, उनका वास्तविक इरादा वास्तव में इस तरह के मामलों के लिए नहीं है। वे वास्तव में बहुत अधिक उपयोगी हैं, जैसा कि अन्य संभावित उपयोगों की तुलना में एकत्रीकरण पाइपलाइन का उपयोग करके एक नए संग्रह में "रूपांतरण" में दिखाया गया है।
जबकि हम "कर सकते हैं" एक "दृश्य" बनाएं जो $lookup
. जैसी चीज़ों को सक्षम करने के लिए डेटा प्रकारों को रूपांतरित करता है और $graphLookup
काम करने के लिए जहां वास्तविक संग्रह डेटा भिन्न होता है, यह वास्तव में केवल एक "बैंड-सहायता" . है वास्तविक समस्या पर क्योंकि डेटा प्रकार वास्तव में भिन्न नहीं होने चाहिए, और वास्तव में स्थायी रूप से परिवर्तित होने चाहिए।
"दृश्य" का उपयोग करने का वास्तव में अर्थ है कि निर्माण के लिए एकत्रीकरण पाइपलाइन को प्रत्येक को प्रभावी ढंग से चलाने की आवश्यकता है समय "संग्रह" (वास्तव में एक "दृश्य") का उपयोग किया जाता है, जो एक वास्तविक ओवरहेड बनाता है।
ओवरहेड से बचना आमतौर पर एक डिज़ाइन लक्ष्य होता है, इसलिए इस तरह की डेटा स्टोरेज गलतियों को ठीक करना आपके एप्लिकेशन से वास्तविक प्रदर्शन प्राप्त करने के लिए अनिवार्य है, न कि केवल "क्रूर फोर्स" के साथ काम करने के लिए जो केवल चीजों को धीमा कर देगा।
एक अधिक सुरक्षित "रूपांतरण" स्क्रिप्ट जो प्रत्येक सरणी तत्व के लिए "मिलान" अपडेट लागू करती है। यहाँ कोड के लिए NodeJS v10.x और नवीनतम रिलीज़ MongoDB नोड ड्राइवर 3.1.x:
की आवश्यकता हैconst { MongoClient, ObjectID: ObjectId } = require('mongodb');
const EJSON = require('mongodb-extended-json');
const uri = 'mongodb://localhost/';
const log = data => console.log(EJSON.stringify(data, undefined, 2));
(async function() {
try {
const client = await MongoClient.connect(uri);
let db = client.db('test');
let coll = db.collection('strcoll');
let fields = ["ancestors", "children"];
let cursor = coll.find({
$or: fields.map(f => ({ [`${f}._id`]: { "$type": "string" } }))
}).project(fields.reduce((o,f) => ({ ...o, [f]: 1 }),{}));
let batch = [];
for await ( let { _id, ...doc } of cursor ) {
let $set = {};
let arrayFilters = [];
for ( const f of fields ) {
if ( doc.hasOwnProperty(f) ) {
$set = { ...$set,
...doc[f].reduce((o,{ _id },i) =>
({ ...o, [`${f}.$[${f.substr(0,1)}${i}]._id`]: ObjectId(_id) }),
{})
};
arrayFilters = [ ...arrayFilters,
...doc[f].map(({ _id },i) =>
({ [`${f.substr(0,1)}${i}._id`]: _id }))
];
}
}
if (arrayFilters.length > 0)
batch = [ ...batch,
{ updateOne: { filter: { _id }, update: { $set }, arrayFilters } }
];
if ( batch.length > 1000 ) {
let result = await coll.bulkWrite(batch);
batch = [];
}
}
if ( batch.length > 0 ) {
log({ batch });
let result = await coll.bulkWrite(batch);
log({ result });
}
await client.close();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
सात दस्तावेज़ों के लिए इस तरह के थोक संचालन का उत्पादन और निष्पादन करता है:
{
"updateOne": {
"filter": {
"_id": {
"$oid": "5afe5763419503c46544e272"
}
},
"update": {
"$set": {
"children.$[c0]._id": {
"$oid": "5afe5763419503c46544e273"
}
}
},
"arrayFilters": [
{
"c0._id": "5afe5763419503c46544e273"
}
]
}
},
{
"updateOne": {
"filter": {
"_id": {
"$oid": "5afe5763419503c46544e273"
}
},
"update": {
"$set": {
"ancestors.$[a0]._id": {
"$oid": "5afe5763419503c46544e272"
},
"children.$[c0]._id": {
"$oid": "5afe5763419503c46544e277"
}
}
},
"arrayFilters": [
{
"a0._id": "5afe5763419503c46544e272"
},
{
"c0._id": "5afe5763419503c46544e277"
}
]
}
},
{
"updateOne": {
"filter": {
"_id": {
"$oid": "5afe5763419503c46544e274"
}
},
"update": {
"$set": {
"children.$[c0]._id": {
"$oid": "5afe5763419503c46544e277"
}
}
},
"arrayFilters": [
{
"c0._id": "5afe5763419503c46544e277"
}
]
}
},
{
"updateOne": {
"filter": {
"_id": {
"$oid": "5afe5763419503c46544e275"
}
},
"update": {
"$set": {
"children.$[c0]._id": {
"$oid": "5afe5763419503c46544e276"
}
}
},
"arrayFilters": [
{
"c0._id": "5afe5763419503c46544e276"
}
]
}
},
{
"updateOne": {
"filter": {
"_id": {
"$oid": "5afe5763419503c46544e276"
}
},
"update": {
"$set": {
"ancestors.$[a0]._id": {
"$oid": "5afe5763419503c46544e275"
},
"children.$[c0]._id": {
"$oid": "5afe5763419503c46544e277"
}
}
},
"arrayFilters": [
{
"a0._id": "5afe5763419503c46544e275"
},
{
"c0._id": "5afe5763419503c46544e277"
}
]
}
},
{
"updateOne": {
"filter": {
"_id": {
"$oid": "5afe5763419503c46544e277"
}
},
"update": {
"$set": {
"ancestors.$[a0]._id": {
"$oid": "5afe5763419503c46544e273"
},
"ancestors.$[a1]._id": {
"$oid": "5afe5763419503c46544e274"
},
"ancestors.$[a2]._id": {
"$oid": "5afe5763419503c46544e276"
}
}
},
"arrayFilters": [
{
"a0._id": "5afe5763419503c46544e273"
},
{
"a1._id": "5afe5763419503c46544e274"
},
{
"a2._id": "5afe5763419503c46544e276"
}
]
}
},
{
"updateOne": {
"filter": {
"_id": {
"$oid": "5afe5764419503c46544e278"
}
},
"update": {
"$set": {
"children.$[c0]._id": {
"$oid": "5afe5763419503c46544e272"
}
}
},
"arrayFilters": [
{
"c0._id": "5afe5763419503c46544e272"
}
]
}
}