इसे संभालने का तरीका आसान नहीं है, क्योंकि "अप्सर्ट्स" को "एरेज़" में आइटम जोड़ने से आसानी से अवांछित परिणाम मिल सकते हैं। यह इस बात पर भी निर्भर करता है कि क्या आप तर्क को अन्य फ़ील्ड सेट करना चाहते हैं जैसे "काउंटर" यह दर्शाता है कि एक सरणी के भीतर कितने संपर्क हैं, जिसे आप केवल बढ़ाना/घटाना चाहते हैं क्योंकि आइटम क्रमशः जोड़े या निकाले जाते हैं।
हालांकि, सबसे सरल मामले में, यदि "संपर्कों" में केवल एक ही मान होता है जैसे कि ObjectId
दूसरे संग्रह से लिंक करना, फिर $addToSet
संशोधक अच्छी तरह से काम करता है, जब तक कि कोई "काउंटर" शामिल न हो:
Client.findOneAndUpdate(
{ "clientName": clientName },
{ "$addToSet": { "contacts": contact } },
{ "upsert": true, "new": true },
function(err,client) {
// handle here
}
);
और यह सब ठीक है क्योंकि आप केवल यह देखने के लिए परीक्षण कर रहे हैं कि क्या कोई दस्तावेज़ "क्लाइंटनाम" पर मेल खाता है, यदि इसे ऊपर नहीं रखा गया है। मैच है या नहीं, $addToSet
ऑपरेटर अद्वितीय "एकवचन" मूल्यों का ध्यान रखेगा, कोई भी "ऑब्जेक्ट" होने के नाते जो वास्तव में अद्वितीय है।
मुश्किलें वहीं आती हैं जहां आपके पास कुछ ऐसा होता है:
{ "firstName": "John", "lastName": "Smith", "age": 37 }
पहले से ही संपर्क सरणी में है, और फिर आप कुछ ऐसा करना चाहते हैं:
{ "firstName": "John", "lastName": "Smith", "age": 38 }
जहां आपका वास्तविक इरादा यह है कि यह "वही" जॉन स्मिथ है, और यह सिर्फ इतना है कि "उम्र" अलग नहीं है। आदर्श रूप से आप केवल "अपडेट" करना चाहते हैं कि सरणी प्रविष्टि अंत में एक नया सरणी या एक नया दस्तावेज़ बनाएं।
इसे .findOneAndUpdate()
. के साथ काम करना जहां आप चाहते हैं कि अपडेट किए गए दस्तावेज़ को वापस करना मुश्किल हो सकता है। इसलिए यदि आप वास्तव में संशोधित दस्तावेज़ को प्रतिक्रिया में नहीं चाहते हैं, तो बल्क ऑपरेशन एपीआई
MongoDB और कोर ड्राइवर यहां सबसे अधिक मदद करते हैं।
कथनों को ध्यान में रखते हुए:
var bulk = Client.collection.initializeOrderedBulkOP();
// First try the upsert and set the array
bulk.find({ "clientName": clientName }).upsert().updateOne({
"$setOnInsert": {
// other valid client info in here
"contacts": [contact]
}
});
// Try to set the array where it exists
bulk.find({
"clientName": clientName,
"contacts": {
"$elemMatch": {
"firstName": contact.firstName,
"lastName": contact.lastName
}
}
}).updateOne({
"$set": { "contacts.$": contact }
});
// Try to "push" the array where it does not exist
bulk.find({
"clientName": clientName,
"contacts": {
"$not": { "$elemMatch": {
"firstName": contact.firstName,
"lastName": contact.lastName
}}
}
}).updateOne({
"$push": { "contacts": contact }
});
bulk.execute(function(err,response) {
// handle in here
});
यह अच्छा है क्योंकि यहां बल्क ऑपरेशंस का मतलब है कि यहां सभी स्टेटमेंट सर्वर को एक बार में भेजे जाते हैं और केवल एक ही प्रतिक्रिया होती है। यहां यह भी ध्यान दें कि यहां तर्क का अर्थ है कि अधिकतम केवल दो ऑपरेशन वास्तव में कुछ भी संशोधित करेंगे।
पहले उदाहरण में, $setOnInsert
कोड>
संशोधक यह सुनिश्चित करता है कि जब दस्तावेज़ सिर्फ एक मैच हो तो कुछ भी नहीं बदला है। चूंकि यहां केवल संशोधन उस ब्लॉक के भीतर हैं, यह केवल उस दस्तावेज़ को प्रभावित करता है जहां "अप्सर्ट" होता है।
अगले दो कथनों पर भी ध्यान दें कि आप फिर से "अपरर्ट" करने का प्रयास नहीं करते हैं। यह मानता है कि पहला कथन संभवतः सफल था जहाँ उसे होना था, या अन्यथा कोई फर्क नहीं पड़ा।
कोई "अप्सर्ट" नहीं होने का दूसरा कारण यह है कि सरणी में तत्व की उपस्थिति का परीक्षण करने के लिए आवश्यक शर्तों को एक नए दस्तावेज़ के "अप्सर्ट" की ओर ले जाया जाएगा जब वे मिले नहीं थे। यह वांछित नहीं है, इसलिए कोई "अपर्ट" नहीं है।
वे वास्तव में क्या करते हैं क्रमशः जांचते हैं कि सरणी तत्व मौजूद है या नहीं, और या तो मौजूदा तत्व को अपडेट करें या एक नया बनाएं। इसलिए कुल मिलाकर, सभी ऑपरेशनों का मतलब है कि आप या तो "एक बार" या अधिकतम "दो बार" संशोधित करते हैं, जहां एक अपर्ट हुआ। संभव "दो बार" बहुत कम ओवरहेड बनाता है और कोई वास्तविक समस्या नहीं होती है।
साथ ही तीसरे कथन में $not
ऑपरेटर $elemMatchके तर्क को उलट देता है कोड>
यह निर्धारित करने के लिए कि क्वेरी शर्त के साथ कोई सरणी तत्व मौजूद नहीं है।
इसका अनुवाद .findOneAndUpdate()
. के साथ करना कुछ ज्यादा ही मुद्दा बन जाता है। न केवल यह "सफलता" है जो अब मायने रखती है, यह यह भी निर्धारित करती है कि अंतिम सामग्री कैसे लौटाई जाती है।
तो यहां सबसे अच्छा विचार घटनाओं को "श्रृंखला" में चलाने के लिए है, और फिर अंत "अपडेटेड" फॉर्म को वापस करने के लिए परिणाम के साथ थोड़ा जादू करना है।
हम यहां जिस सहायता का उपयोग करेंगे, वह है async.waterfall और lodash पुस्तकालय:
var _ = require('lodash'); // letting you know where _ is coming from
async.waterfall(
[
function(callback) {
Client.findOneAndUpdate(
{ "clientName": clientName },
{
"$setOnInsert": {
// other valid client info in here
"contacts": [contact]
}
},
{ "upsert": true, "new": true },
callback
);
},
function(client,callback) {
Client.findOneAndUpdate(
{
"clientName": clientName,
"contacts": {
"$elemMatch": {
"firstName": contact.firstName,
"lastName": contact.lastName
}
}
},
{ "$set": { "contacts.$": contact } },
{ "new": true },
function(err,newClient) {
client = client || {};
newClient = newClient || {};
client = _.merge(client,newClient);
callback(err,client);
}
);
},
function(client,callback) {
Client.findOneAndUpdate(
{
"clientName": clientName,
"contacts": {
"$not": { "$elemMatch": {
"firstName": contact.firstName,
"lastName": contact.lastName
}}
}
},
{ "$push": { "contacts": contact } },
{ "new": true },
function(err,newClient) {
newClient = newClient || {};
client = _.merge(client,newClient);
callback(err,client);
}
);
}
],
function(err,client) {
if (err) throw err;
console.log(client);
}
);
यह उसी तर्क का अनुसरण करता है जैसा कि पहले था कि उनमें से केवल दो या एक कथन वास्तव में इस संभावना के साथ कुछ भी करने जा रहा है कि "नया" दस्तावेज़ लौटाया जा रहा है null
. यहां "झरना" प्रत्येक चरण से अगले चरण में परिणाम देता है, जिसमें अंत भी शामिल है जहां कोई भी त्रुटि तुरंत शाखा होगी।
इस मामले में null
एक खाली वस्तु के लिए स्वैप किया जाएगा {}
और _.merge()
विधि दो वस्तुओं को प्रत्येक बाद के चरण में एक में जोड़ देगी। यह आपको अंतिम परिणाम देता है जो कि संशोधित वस्तु है, इससे कोई फर्क नहीं पड़ता कि किस पूर्ववर्ती संचालन ने वास्तव में कुछ भी किया है।
बेशक, $pull
. के लिए एक अलग हेरफेर की आवश्यकता होगी , और आपके प्रश्न में इनपुट डेटा अपने आप में एक ऑब्जेक्ट फॉर्म के रूप में है। लेकिन वे वास्तव में अपने आप में उत्तर हैं।
इससे आपको कम से कम इस बात की शुरुआत करनी चाहिए कि आप अपने अपडेट पैटर्न तक कैसे पहुंचें।