इसलिए मैंने टिप्पणियों में एक प्रश्न पूछा लेकिन लगता है कि आप चले गए हैं, इसलिए मुझे लगता है कि मैं केवल उन तीन संभावित मामलों का उत्तर देता हूं जो मुझे दिखाई देते हैं।
शुरू करने के लिए, मुझे यकीन नहीं है कि नेस्टेड सरणियों के भीतर दिखाए गए तत्व केवल हैं सरणी के भीतर या वास्तव में तत्व यदि arrayToDelete
केवल है उन तत्वों में मौजूद क्षेत्र। तो मूल रूप से मुझे सार की आवश्यकता है थोड़ा और उस मामले को शामिल करें:
{
field: 'value',
field2: 'value',
scan: [
[
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
[
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
[
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
]
}
केस 1 - इनर ऐरे एलिमेंट्स को हटा दें जहां फील्ड मौजूद है
यह $pull
. का उपयोग करेगा ऑपरेटर के बाद से वही है जो सरणी तत्वों को हटाता है पूरी तरह से। आप इसे आधुनिक MongoDB में इस तरह के एक बयान के साथ करते हैं:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
)
यह इस तरह से सभी मेल खाने वाले दस्तावेज़ों को बदल देता है:
{
"_id" : ObjectId("5ca1c36d9e31550a618011e2"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
]
]
}
इसलिए हर वह तत्व जिसमें वह फ़ील्ड था, अब हटा दिया गया है।
केस 2 - बस मिलान किए गए फ़ील्ड को आंतरिक तत्वों से हटा दें
यह वह जगह है जहां आप $unset
. का उपयोग करते हैं . यह "हार्ड इंडेक्सेड" . से थोड़ा अलग है संस्करण जो आप कर रहे थे:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[].$[].arrayToDelete": "" } }
)
जो सभी मेल खाने वाले दस्तावेज़ों को बदल देता है:
{
"_id" : ObjectId("5ca1c4c49e31550a618011e3"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"anotherField" : "a"
},
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
}
],
[
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
}
],
[
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
}
]
]
}
तो सब कुछ अभी भी है, लेकिन प्रत्येक आंतरिक सरणी दस्तावेज़ से केवल पहचाने गए फ़ील्ड हटा दिए गए हैं।
केस 3 - आप वास्तव में सरणी से "सब कुछ" हटाना चाहते थे।
जो वास्तव में $set
. का उपयोग करने का एक साधारण मामला है और जो कुछ पहले था उसे मिटा देना:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$set": { "scan": [] } }
)
जहां परिणामों की बहुत अच्छी उम्मीद की जानी चाहिए:
{
"_id" : ObjectId("5ca1c5c59e31550a618011e4"),
"field" : "value",
"field2" : "value",
"scan" : [ ]
}
तो ये सब क्या कर रहे हैं?
सबसे पहली चीज़ जो आपको देखनी चाहिए वह है क्वेरी विधेय . यह सुनिश्चित करने के लिए आम तौर पर एक अच्छा विचार है कि आप मेल नहीं खा रहे हैं और यहां तक कि "प्रयास कर रहे हैं" उन दस्तावेजों पर अद्यतन शर्तों को पूरा करने के लिए जिनमें उस पैटर्न के साथ डेटा भी शामिल नहीं है जिसे आप अपडेट करना चाहते हैं। नेस्टेड सरणियाँ मुश्किल हैं सबसे अच्छा, और जहां व्यावहारिक हो, आपको वास्तव में उनसे बचना चाहिए, जैसा कि आप अक्सर "वास्तव में मतलब" . करते हैं वास्तव में आप जो "सोचते हैं" . का प्रतिनिधित्व करते हुए अतिरिक्त विशेषताओं के साथ एक एकल सरणी में प्रतिनिधित्व करते हैं घोंसला वास्तव में आपके लिए कर रहा है।
लेकिन सिर्फ इसलिए कि वे कठिन हैं इसका मतलब असंभव नहीं है . बस आपको $elemMatch
. को समझने की जरूरत है :
db.colelction.find(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
}}
)
यह मूल है find()
उदाहरण, जो $elemMatch
. के आधार पर मेल खाता है बाहरी . के लिए शर्त सरणी एक और $elemMatch
का उपयोग करती है आंतरिक . में किसी अन्य शर्त का मिलान करने के लिए सरणी। भले ही यह "प्रकट होता है" एक विलक्षण विधेय होना। कुछ इस तरह:
"scan.arrayToDelete": { "$exists": true }
बस काम नहीं करेगा। न तो होगा:
"scan..arrayToDelete": { "$exists": true }
"डबल डॉट" के साथ ..
क्योंकि यह मूल रूप से मान्य नहीं है।
वह क्वेरी विधेय है "दस्तावेज़ों" से मेल खाने के लिए जिन्हें संसाधित करने की आवश्यकता है, लेकिन बाकी वास्तव में यह निर्धारित करने के लिए लागू होते हैं कि *कौन से हिस्से अपडेट करने हैं"।
मामले 1 . में $pull
. के लिए आंतरिक . से सरणी, हमें सबसे पहले यह पहचानने में सक्षम होना चाहिए कि बाहरी . के कौन से तत्व हैं सरणी में अद्यतन करने के लिए डेटा होता है। यही है "scan.$[a]"
स्थिति फ़िल्टर किए गए $[<identifier>]
. का उपयोग करके काम कर रहा है ऑपरेटर।
वह ऑपरेटर मूल रूप से स्थानांतरित करता है मिलान किए गए सूचकांक (तो कई उनमें से) सरणी में दूसरे विधेय . में जिसे update
. के तीसरे खंड में परिभाषित किया गया है arrayFilters
. के साथ स्टाइल कमांड खंड। यह खंड मूल रूप से नामित पहचानकर्ता के दृष्टिकोण से मिलने वाली शर्तों को परिभाषित करता है।
इस मामले में "पहचानकर्ता" को a
. नाम दिया गया है , और वह उपसर्ग है जिसका उपयोग arrayFilters
. में किया जाता है प्रविष्टि:
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
वास्तविक अद्यतन विवरण . के संदर्भ में लिया गया भाग:
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
फिर "a"
. के दृष्टिकोण से बाहरी . के लिए पहचानकर्ता होने के नाते सरणी तत्व पहले "scan"
. से आवक , तो वही शर्तें लागू होती हैं जो मूल क्वेरी विधेय . के लिए लागू होती हैं लेकिन "भीतर" . से पहला $elemMatch
बयान। तो आप मूल रूप से इसे पहले से ही "अंदर देखने" के दृष्टिकोण से "एक क्वेरी के भीतर क्वेरी" के रूप में सोच सकते हैं प्रत्येक बाहरी . की सामग्री तत्व।
उसी टोकन के द्वारा $pull
"क्वेरी के भीतर क्वेरी" . की तरह कार्य करता है इसमें अपने स्वयं के तर्क भी सरणी के तत्व के परिप्रेक्ष्य से लागू होते हैं। इसलिए केवल arrayToDelete
इसके बजाय मौजूद फ़ील्ड:
// This would be wrong! and do nothing :(
{
"$pull": {
"scan.$[a]": { "$elemMatch": { "arrayToDelete": { "$exists": true } } }
}
}
लेकिन यह सब $pull
. के लिए विशिष्ट है , और अन्य चीजों के मामले अलग हैं:
केस 2 देखता है कि आप केवल $unset
को कहां देखना चाहते हैं नामित क्षेत्र। बहुत आसान लगता है जैसा कि आप सिर्फ मैदान का नाम देते हैं, है ना? ठीक नहीं, क्योंकि जो हम पहले जानते हैं, उससे निम्नलिखित स्पष्ट रूप से सही नहीं है:
{ "$unset": { "scan.arrayToDelete": "" } } // Not right :(
और निश्चित रूप से हर चीज के लिए ऐरे इंडेक्स को नोट करना सिर्फ एक दर्द है:
{ "$unset": {
"scan.0.0.arrayToDelete": "",
"scan.0.1.arrayToDelete": "",
"scan.0.2.arrayToDelete": "",
"scan.0.3.arrayToDelete": "", // My fingers are tired :-<
} }
यह स्थितीय सभी $[]
. का कारण है ऑपरेटर। यह थोड़ा अधिक है "क्रूर बल" स्थितीय फ़िल्टर किए गए $[<identifier>]
. की तुलना में उसमें एक और विधेय . से मेल खाने के बजाय arrayFilters
. के भीतर प्रदान किया गया , यह जो सरलता से करता है वह सब कुछ . पर लागू होता है उस "इंडेक्स" पर सरणी सामग्री के भीतर। यह मूल रूप से "सभी अनुक्रमणिका" . कहने का एक तरीका है हर एक को भयानक . की तरह लिखे बिना ऊपर दिखाया गया मामला।
तो यह सभी मामलों के लिए नहीं है , लेकिन यह निश्चित रूप से एक $unset
. के अनुकूल है चूंकि इसका एक बहुत विशिष्ट पथ नामकरण . है जो निश्चित रूप से मायने नहीं रखता अगर वह पथ सरणी के हर एक तत्व से मेल नहीं खाता।
आप कर सकते थे अभी भी एक arrayFilters
का उपयोग करें और एक स्थितीय फ़िल्टर किया गया $[<identifier>]
, लेकिन यहाँ यह अधिक होगा। साथ ही दूसरे दृष्टिकोण को प्रदर्शित करने में कोई हर्ज नहीं है।
लेकिन निश्चित रूप से यह समझने योग्य है कैसे वास्तव में वह कथन दिखाई देगा, इसलिए:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[a].$[b].arrayToDelete": "" } },
{
"arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } },
{ "b.arrayToDelete": { "$exists": true } },
]
}
)
यह ध्यान में रखते हुए कि "b.arrayToDelete"
हो सकता है कि वह वह न हो जिसकी आप पहली बार अपेक्षा करते हैं, लेकिन "scan.$[a].$[b]
में स्थिति को देखते हुए यह वास्तव में b
. से समझ में आना चाहिए तत्व का नाम "डॉट नोटेशन" के माध्यम से दिखाया जाएगा जैसा कि दिखाया गया है। और वास्तव में दोनों ही मामलों में। फिर भी, एक $unset
वैसे भी केवल नामित फ़ील्ड पर ही लागू होगा, इसलिए चयन मानदंड की वास्तव में आवश्यकता नहीं है।
और केस 3 . वैसे यह बहुत आसान है अगर आपको ज़रूरत नहीं है इस सामग्री को हटाने के बाद सरणी में कुछ और रखने के लिए (यानी एक $pull
जहां इससे मेल खाने वाले फ़ील्ड केवल . थे वहाँ चीज़ें, या कोई $unset
उस मामले के लिए), तो बस किसी और चीज के साथ खिलवाड़ न करें और बस सरणी को मिटा दें ।
यह एक महत्वपूर्ण अंतर है यदि आप उस बिंदु के अनुसार स्पष्ट करते हैं कि नामित फ़ील्ड वाले दस्तावेज़ जहां केवल हैं नेस्टेड सरणियों के भीतर तत्व, और वास्तव में नामित कुंजी केवल . थी दस्तावेज़ों में मौजूद चीज़।
तर्क के साथ कि $pull
. का उपयोग करना जैसा कि यहां दिखाया गया है और उन शर्तों के तहत आपको मिलेगा:
{
"_id" : ObjectId("5ca321909e31550a618011e6"),
"field" : "value",
"field2" : "value",
"scan" : [
[ ],
[ ],
[ ]
]
}
या $unset
. के साथ :
{
"_id" : ObjectId("5ca322bc9e31550a618011e7"),
"field" : "value",
"field2" : "value",
"scan" : [
[{ }, { }, { }, { }],
[{ }, { }, { }, { }],
[{ }, { }, { }, { }]
]
}
जिनमें से दोनों स्पष्ट रूप से वांछनीय नहीं हैं। तो यह यो कारण है यदि arrayToDelete
फ़ील्ड केवल केवल था सामग्री जो वहां बिल्कुल भी थी, फिर सभी को हटाने . का सबसे तार्किक तरीका सरणी को खाली के साथ बदलने के लिए बस है। या वास्तव में $unset
संपूर्ण दस्तावेज़ संपत्ति।
हालांकि ध्यान दें कि ये सभी "फैंसी चीजें" ( $set
. के अपवाद के साथ निश्चित रूप से) के लिए आवश्यक है कि आपके पास कम से कम MongoDB 3.6 होना चाहिए इस कार्यक्षमता का उपयोग करने के लिए उपलब्ध है।
इस घटना में कि आप अभी भी एक पुराने संस्करण MongoDB चला रहे हैं (और लेखन की तारीख के अनुसार, आपको वास्तव में ऐसा नहीं होना चाहिए क्योंकि आपका आधिकारिक समर्थन इस तिथि से केवल 5 महीनों में समाप्त हो जाता है) फिर अन्य मौजूदा उत्तर एकाधिक अपडेट कैसे करें मोंगोडब में ऐरे एलिमेंट्स वास्तव में आपके लिए हैं।