सबसे छोटे उत्तर में, यह "हां" और "नहीं" दोनों है।
वास्तव में अलग-अलग सरणी तत्वों से मिलान करने और उन्हें एक ही कथन में अलग-अलग मानों के साथ अपडेट करने का एक तरीका है, क्योंकि आप वास्तव में "एकाधिक" arrayFilters
प्रदान कर सकते हैं शर्तों और उन पहचानकर्ताओं का उपयोग अपने अपडेट स्टेटमेंट में करें।
यहां आपके विशेष नमूने के साथ समस्या यह है कि आपके "परिवर्तन सेट" (अंतिम एक) में से एक प्रविष्टि वास्तव में वर्तमान में मौजूद किसी भी सरणी सदस्य से मेल नहीं खाती है। यहां "अनुमानित" कार्रवाई $push
वह नया गैर-मिलान सदस्य उस सरणी में जहां यह नहीं मिला। हालांकि वह विशेष क्रिया नहीं "एकल ऑपरेशन" . में किया जाना है , लेकिन आप bulkWrite()
उस मामले को कवर करने के लिए "एकाधिक" कथन जारी करना।
विभिन्न सरणी स्थितियों का मिलान
यह समझाते हुए कि बिंदुओं में, अपने "परिवर्तन सेट" में पहले दो आइटमों पर विचार करें। आप एक "एकल" . लागू कर सकते हैं एकाधिक arrayFilters
. के साथ अद्यतन विवरण इस तरह:
db.avail_rates_copy.updateOne(
{ "_id": 12345 },
{
"$set": {
"rates.$[one]": {
"productId" : NumberInt(1234),
"rate" : 400.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
},
"rates.$[two]": {
"productId" : NumberInt(1234),
"rate" : 500.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
},
{
"arrayFilters": [
{
"one.productId": NumberInt(1234),
"one.rateCardId": NumberInt(1),
"one.month": NumberInt(201801)
},
{
"two.productId": NumberInt(1234),
"two.rateCardId": NumberInt(1),
"two.month": NumberInt(201802)
}
]
}
)
यदि आप इसे चलाते हैं तो आप देखेंगे कि संशोधित दस्तावेज़ बन जाता है:
{
"_id" : 12345,
"_class" : "com.example.ProductRates",
"rates" : [
{ // Matched and changed this by one
"productId" : 1234,
"rate" : 400,
"rateCardId" : 1,
"month" : 201801
},
{ // And this as two
"productId" : 1234,
"rate" : 500,
"rateCardId" : 1,
"month" : 201802
},
{
"productId" : 1234,
"rate" : 400,
"rateCardId" : 2,
"month" : 201803
},
{
"productId" : 1235,
"rate" : 500,
"rateCardId" : 1,
"month" : 201801
},
{
"productId" : 1235,
"rate" : 234,
"rateCardId" : 2,
"month" : 201803
}
]
}
यहां ध्यान दें कि आप प्रत्येक "पहचानकर्ता" को arrayFilters
. की सूची में निर्दिष्ट करते हैं इस तरह के तत्व से मेल खाने के लिए कई शर्तों के साथ:
{
"one.productId": NumberInt(1234),
"one.rateCardId": NumberInt(1),
"one.month": NumberInt(201801)
},
इसलिए प्रत्येक "शर्त" प्रभावी रूप से इस प्रकार मैप करती है:
<identifier>.<property>
तो यह "दरों"
. को देखना जानता है $[
:
"rates.$[one]"
और "rates"
. के प्रत्येक तत्व को देखता है शर्तों से मेल खाने के लिए। तो "एक"
पहचानकर्ता "one"
. से पहले की शर्तों से मेल खाएगा और इसी तरह "two"
. के साथ पहले की शर्तों के दूसरे सेट के लिए , इसलिए वास्तविक अद्यतन विवरण केवल उन पर लागू होता है जो पहचानकर्ता को निर्दिष्ट शर्तों से मेल खाते हैं।
अगर आप सिर्फ "दरों"
. चाहते थे संपत्ति पूरी वस्तु के विपरीत है, तो आप बस इस रूप में नोट करें:
{ "$set": { "rates.$[one].rate": 400, "rates.$[two].rate": 500 } }
मेल न खाने वाली वस्तुओं को जोड़ना
तो पहला भाग समझने में अपेक्षाकृत सरल है, लेकिन जैसा कि कहा गया है $पुश
"तत्व जो वहां नहीं है" के लिए एक अलग मामला है, क्योंकि हमें मूल रूप से "दस्तावेज़" स्तर पर एक क्वेरी शर्त की आवश्यकता होती है ताकि यह निर्धारित किया जा सके कि एक सरणी तत्व "गायब" है।
इसका अनिवार्य रूप से मतलब यह है कि आपको $पुश
प्रत्येक सरणी तत्व की तलाश में यह देखने के लिए कि यह मौजूद है या नहीं। जब यह मौजूद नहीं होता है, तो दस्तावेज़ एक मैच होता है और $पुश
किया जाता है।
यहीं पर bulkWrite()
चलन में आता है, और आप "चेंज सेट" में प्रत्येक तत्व के लिए ऊपर दिए गए हमारे पहले ऑपरेशन में एक अतिरिक्त अपडेट जोड़कर इसका उपयोग करते हैं:
db.avail_rates_copy.bulkWrite(
[
{ "updateOne": {
"filter": { "_id": 12345 },
"update": {
"$set": {
"rates.$[one]": {
"productId" : NumberInt(1234),
"rate" : 400.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
},
"rates.$[two]": {
"productId" : NumberInt(1234),
"rate" : 500.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
},
"rates.$[three]": {
"productId" : NumberInt(1235),
"rate" : 700.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
},
"arrayFilters": [
{
"one.productId": NumberInt(1234),
"one.rateCardId": NumberInt(1),
"one.month": NumberInt(201801)
},
{
"two.productId": NumberInt(1234),
"two.rateCardId": NumberInt(1),
"two.month": NumberInt(201802)
},
{
"three.productId": NumberInt(1235),
"three.rateCardId": NumberInt(1),
"three.month": NumberInt(201802)
}
]
}},
{ "updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId" : NumberInt(1234),
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
}
}
}
},
"update": {
"$push": {
"rates": {
"productId" : NumberInt(1234),
"rate" : 400.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201801)
}
}
}
}},
{ "updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId" : NumberInt(1234),
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
}
},
"update": {
"$push": {
"rates": {
"productId" : NumberInt(1234),
"rate" : 500.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
}
}},
{ "updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId" : NumberInt(1235),
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
}
},
"update": {
"$push": {
"rates": {
"productId" : NumberInt(1235),
"rate" : 700.0,
"rateCardId": NumberInt(1),
"month" : NumberInt(201802)
}
}
}
}}
],
{ "ordered": true }
)
$elemMatch
पर ध्यान दें
क्वेरी फ़िल्टर के साथ, क्योंकि यह "एकाधिक स्थितियों" द्वारा एक सरणी तत्व से मेल खाने की आवश्यकता है। हमें arrayFilters
. पर इसकी आवश्यकता नहीं थी प्रविष्टियां क्योंकि वे केवल प्रत्येक सरणी आइटम को देखें, जिस पर वे पहले से लागू हैं, लेकिन "क्वेरी" के रूप में शर्तों के लिए $elemMatch
जैसा कि साधारण "डॉट नोटेशन" गलत मिलान लौटाएगा।
यह भी देखें $not
ऑपरेटर का उपयोग यहां $elemMatchको "नकारात्मक" करने के लिए किया जाता है। कोड>
, क्योंकि हमारी वास्तविक शर्तें केवल उस दस्तावेज़ से मेल खाने के लिए हैं जो "इसमें सरणी तत्व से मेल नहीं खाता है" प्रदान की गई शर्तों के अनुसार, और यही एक नए तत्व को जोड़ने के लिए चयन को सही ठहराता है।
और सर्वर को जारी किया गया वह एकल कथन अनिवार्य रूप से चार . का प्रयास करता है मिलान किए गए सरणी तत्वों को अद्यतन करने का प्रयास करने के लिए एक के रूप में संचालन अद्यतन करें, और दूसरा तीन . में से प्रत्येक के लिए "सेट बदलें" $पुश
का प्रयास कर रहा है जहां दस्तावेज़ "चेंज सेट" में सरणी तत्व के लिए शर्तों से मेल नहीं खाता पाया गया था।
इसलिए परिणाम अपेक्षित है:
{
"_id" : 12345,
"_class" : "com.example.ProductRates",
"rates" : [
{ // matched and updated
"productId" : 1234,
"rate" : 400,
"rateCardId" : 1,
"month" : 201801
},
{ // matched and updated
"productId" : 1234,
"rate" : 500,
"rateCardId" : 1,
"month" : 201802
},
{
"productId" : 1234,
"rate" : 400,
"rateCardId" : 2,
"month" : 201803
},
{
"productId" : 1235,
"rate" : 500,
"rateCardId" : 1,
"month" : 201801
},
{
"productId" : 1235,
"rate" : 234,
"rateCardId" : 2,
"month" : 201803
},
{ // This was appended
"productId" : 1235,
"rate" : 700,
"rateCardId" : 1,
"month" : 201802
}
]
}
इस पर निर्भर करता है कि कितने तत्व वास्तव में bulkWrite()
. से मेल नहीं खाते हैं प्रतिक्रिया रिपोर्ट करेगी कि उनमें से कितने कथन वास्तव में एक दस्तावेज़ से मेल खाते और प्रभावित हुए। इस मामले में यह 2
है मिलान और संशोधित, चूंकि "पहला" अपडेट ऑपरेशन मौजूदा सरणी प्रविष्टियों से मेल खाता है, और "अंतिम" परिवर्तन अपडेट मेल खाता है कि दस्तावेज़ में सरणी प्रविष्टि शामिल नहीं है और $push
संशोधित करने के लिए।
निष्कर्ष
तो वहां आपके पास संयुक्त दृष्टिकोण है, जहां:
-
आपके प्रश्न में "अपडेट" का पहला भाग बहुत आसान है और इसे एकल कथन में किया जा सकता है , जैसा कि पहले खंड में दिखाया गया है।
-
दूसरा भाग जहां एक सरणी तत्व है जो "वर्तमान में मौजूद नहीं है" वर्तमान दस्तावेज़ सरणी में, इसके लिए वास्तव में आपको
का उपयोग करने की आवश्यकता है बल्कवाइट ()
एक ही अनुरोध में "एकाधिक" संचालन जारी करने के लिए।
इसलिए अपडेट करें , एकल ऑपरेशन के लिए "YES" है। लेकिन अंतर जोड़ना मतलब कई ऑपरेशन। लेकिन आप दो दृष्टिकोणों को जोड़ सकते हैं जैसा कि यहां दिखाया गया है।
ऐसे कई "फैंसी" तरीके हैं जिनसे आप कोड के साथ "चेंज सेट" सरणी सामग्री के आधार पर इन कथनों का निर्माण कर सकते हैं, इसलिए आपको प्रत्येक सदस्य को "हार्डकोड" करने की आवश्यकता नहीं है।
जावास्क्रिप्ट के लिए एक बुनियादी मामले के रूप में और मोंगो खोल की वर्तमान रिलीज के साथ संगत (जो कुछ हद तक परेशान रूप से ऑब्जेक्ट स्प्रेड ऑपरेटरों का समर्थन नहीं करता है):
db.getCollection('avail_rates_copy').drop();
db.getCollection('avail_rates_copy').insert(
{
"_id" : 12345,
"_class" : "com.example.ProductRates",
"rates" : [
{
"productId" : 1234,
"rate" : 100,
"rateCardId" : 1,
"month" : 201801
},
{
"productId" : 1234,
"rate" : 200,
"rateCardId" : 1,
"month" : 201802
},
{
"productId" : 1234,
"rate" : 400,
"rateCardId" : 2,
"month" : 201803
},
{
"productId" : 1235,
"rate" : 500,
"rateCardId" : 1,
"month" : 201801
},
{
"productId" : 1235,
"rate" : 234,
"rateCardId" : 2,
"month" : 201803
}
]
}
);
var changeSet = [
{
"productId" : 1234,
"rate" : 400.0,
"rateCardId": 1,
"month" : 201801
},
{
"productId" : 1234,
"rate" : 500.0,
"rateCardId": 1,
"month" : 201802
},
{
"productId" : 1235,
"rate" : 700.0,
"rateCardId": 1,
"month" : 201802
}
];
var arrayFilters = changeSet.map((obj,i) =>
Object.keys(obj).filter(k => k != 'rate' )
.reduce((o,k) => Object.assign(o, { [`u${i}.${k}`]: obj[k] }) ,{})
);
var $set = changeSet.reduce((o,r,i) =>
Object.assign(o, { [`rates.$[u${i}].rate`]: r.rate }), {});
var updates = [
{ "updateOne": {
"filter": { "_id": 12345 },
"update": { $set },
arrayFilters
}},
...changeSet.map(obj => (
{ "updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": Object.keys(obj).filter(k => k != 'rate')
.reduce((o,k) => Object.assign(o, { [k]: obj[k] }),{})
}
}
},
"update": {
"$push": {
"rates": obj
}
}
}}
))
];
db.getCollection('avail_rates_copy').bulkWrite(updates,{ ordered: true });
यह गतिशील रूप से "थोक" अद्यतन संचालन की एक सूची तैयार करेगा जो इस तरह दिखेगा:
[
{
"updateOne": {
"filter": {
"_id": 12345
},
"update": {
"$set": {
"rates.$[u0].rate": 400,
"rates.$[u1].rate": 500,
"rates.$[u2].rate": 700
}
},
"arrayFilters": [
{
"u0.productId": 1234,
"u0.rateCardId": 1,
"u0.month": 201801
},
{
"u1.productId": 1234,
"u1.rateCardId": 1,
"u1.month": 201802
},
{
"u2.productId": 1235,
"u2.rateCardId": 1,
"u2.month": 201802
}
]
}
},
{
"updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId": 1234,
"rateCardId": 1,
"month": 201801
}
}
}
},
"update": {
"$push": {
"rates": {
"productId": 1234,
"rate": 400,
"rateCardId": 1,
"month": 201801
}
}
}
}
},
{
"updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId": 1234,
"rateCardId": 1,
"month": 201802
}
}
}
},
"update": {
"$push": {
"rates": {
"productId": 1234,
"rate": 500,
"rateCardId": 1,
"month": 201802
}
}
}
}
},
{
"updateOne": {
"filter": {
"_id": 12345,
"rates": {
"$not": {
"$elemMatch": {
"productId": 1235,
"rateCardId": 1,
"month": 201802
}
}
}
},
"update": {
"$push": {
"rates": {
"productId": 1235,
"rate": 700,
"rateCardId": 1,
"month": 201802
}
}
}
}
}
]
जैसा कि सामान्य उत्तर के "लंबे रूप" में वर्णित किया गया था, लेकिन निश्चित रूप से उन सभी कथनों के निर्माण के लिए इनपुट "सरणी" सामग्री का उपयोग करता है।
आप इस तरह के गतिशील ऑब्जेक्ट निर्माण को किसी भी भाषा में कर सकते हैं, और सभी MongoDB ड्राइवर किसी प्रकार की संरचना के इनपुट को स्वीकार करते हैं जिसे आपको "हेरफेर" करने की अनुमति है, जिसे वास्तव में निष्पादन के लिए सर्वर पर भेजे जाने से पहले BSON में बदल दिया जाता है।