MongoDB - किसी दस्तावेज़ के ऐरे में ऑब्जेक्ट्स को अपडेट करें (नेस्टेड अपडेटिंग)


204

मान लें कि हमारे पास निम्नलिखित संग्रह है, जिसके बारे में मेरे कुछ प्रश्न हैं:

{
    "_id" : ObjectId("4faaba123412d654fe83hg876"),
    "user_id" : 123456,
    "total" : 100,
    "items" : [
            {
                    "item_name" : "my_item_one",
                    "price" : 20
            },
            {
                    "item_name" : "my_item_two",
                    "price" : 50
            },
            {
                    "item_name" : "my_item_three",
                    "price" : 30
            }
    ]
}

1 - मैं "item_name" के लिए मूल्य बढ़ाना चाहता हूं: "my_item_two" और यदि यह मौजूद नहीं है , तो इसे "आइटम" सरणी में जोड़ा जाना चाहिए।

2 - मैं एक ही समय में दो फ़ील्ड कैसे अपडेट कर सकता हूं। उदाहरण के लिए, "my_item_three" के लिए मूल्य बढ़ाएं और उसी समय "कुल" (समान मूल्य के साथ) बढ़ाएं।

मैं इसे MongoDB पक्ष में करना पसंद करता हूं, अन्यथा मुझे क्लाइंट-साइड (Python) में दस्तावेज़ को लोड करना होगा और अद्यतन दस्तावेज़ का निर्माण करना होगा और इसे MongoDB में मौजूदा एक के साथ बदलना होगा।

अद्यतन यह है कि मैं कोशिश की है और ठीक काम करता है अगर वस्तु मौजूद है :

db.test_invoice.update({user_id : 123456 , "items.item_name":"my_item_one"} , {$inc: {"items.$.price": 10}})

लेकिन अगर कुंजी मौजूद नहीं है तो यह कुछ भी नहीं करता है। इसके अलावा यह केवल नेस्टेड ऑब्जेक्ट को अपडेट करता है। इस कमांड के साथ "कुल" फ़ील्ड को अपडेट करने का कोई तरीका नहीं है।


1
मुझे लगता है कि आप इसे मूंगो में नहीं कर सकते हैं, सिवाय इसके कि हो सकता है कि बहुत से दर्द का उपयोग हो। मैंगो डेटा ऑप्स में बहुत सीमित है।
अंती हापला

3
@ हापाला: मोंगोडब में $ inc है और अद्यतन के साथ अद्यतन
jdi

1
@ जेडी हां हां, लेकिन यह यहां बहुत मदद नहीं करता है, लेकिन जो उसे चाहिए वह कई डॉलर है, सशर्त रूप से, और यदि आइटम मौजूद नहीं है, तो एक $ पुश की जरूरत है।
अंती हापला

जवाबों:


237

प्रश्न # 1 के लिए, इसे दो भागों में तोड़ते हैं। सबसे पहले, "my.item_two" के बराबर "आइटम्स .item_name" वाले किसी भी दस्तावेज़ को बढ़ाएँ। इसके लिए आपको स्थितीय "$" ऑपरेटर का उपयोग करना होगा। कुछ इस तरह:

 db.bar.update( {user_id : 123456 , "items.item_name" : "my_item_two" } , 
                {$inc : {"items.$.price" : 1} } , 
                false , 
                true);

ध्यान दें कि यह किसी भी सरणी में केवल पहले से मिलान किए गए उप-साक्षरता को बढ़ाएगा (इसलिए यदि आपके पास "my_item_two" के बराबर "item_name" वाले सरणी में एक और दस्तावेज़ है, तो यह वृद्धि नहीं होगी)। लेकिन यह वही हो सकता है जो आप चाहते हैं।

दूसरा भाग पेचीदा है। हम "my_item_two" के बिना एक नए आइटम को एक सरणी में धकेल सकते हैं:

 db.bar.update( {user_id : 123456, "items.item_name" : {$ne : "my_item_two" }} , 
                {$addToSet : {"items" : {'item_name' : "my_item_two" , 'price' : 1 }} } ,
                false , 
                true);

आपके प्रश्न # 2 के लिए, उत्तर आसान है। "My_item_three" वाले किसी भी दस्तावेज़ में item_three के कुल और मूल्य में वृद्धि करने के लिए, आप एक ही समय में कई क्षेत्रों में $ inc ऑपरेटर का उपयोग कर सकते हैं। कुछ इस तरह:

db.bar.update( {"items.item_name" : {$ne : "my_item_three" }} ,
               {$inc : {total : 1 , "items.$.price" : 1}} ,
               false ,
               true);

उत्तर के लिए धन्यवाद। प्रश्न # 2 का उत्तर एकदम सही है और ठीक काम करता है :) लेकिन प्रश्न # 1 के लिए, समस्या यह है कि आपको दस्तावेज़ को "user_id" द्वारा खोजना चाहिए, न कि "items.item_name" द्वारा। इस मामले में मैं स्थिति संचालक का उपयोग नहीं कर सकता, क्योंकि मैंने खोज क्वेरी में सरणी निर्दिष्ट नहीं की है। इसके अलावा मैं चाहता हूं कि दोनों हिस्से एक शॉट में किए जाएं। (यदि संभव हो तो)
माजिद

अच्छे उदाहरण हैं। मुझे ऐसा लग रहा था कि मैं अपने उत्तर में दिए गए लिंक से इस दिशा को स्पष्ट कर रहा हूं, लेकिन मैं यह कहते हुए प्रतिरोध करता रहा कि वह नहीं देख रहा था। ओपी को लगता है कि कई $ inc करने के तरीके पर उदाहरण देखने की जरूरत है।
12:09

प्रश्न # 1 के लिए, आपको अभी भी प्रत्येक अपडेट के क्वेरी बिट में user_id जोड़कर इसे दो भागों में करने में सक्षम होना चाहिए (मैं तदनुसार उत्तर संशोधित करूंगा)। इस बारे में अधिक सोचना होगा कि क्या एक शॉट में करना संभव है।
मटुलफ

6
अद्यतन विधि में 3rd और 4th पैरामीटर क्या हैं? और क्यों?
स्कमरवार

5
@skmahawar, 3rd और 4th params के बारे में, docs.mongodb.com/manual/reference/method/db.collection.update इंगित करता है कि ये विकल्प क्रमशः "तेज" और "बहु" के लिए हैं। यदि कोई दस्तावेज़ बिना क्वेरी मापदंड से मेल खाता है, तो upsert के लिए, यदि यह सच है, तो एक नया दस्तावेज़ बनाता है। डिफ़ॉल्ट मान गलत है, जो कोई मिलान नहीं होने पर एक नया दस्तावेज़ सम्मिलित नहीं करता है। "बहु के लिए, यदि सही पर सेट है, तो क्वेरी मापदंड को पूरा करने वाले कई दस्तावेज़ों को अपडेट करता है। यदि गलत पर सेट किया गया है, तो एक दस्तावेज़ को अद्यतन करता है। डिफ़ॉल्ट मान। मिथ्या।
माइक स्ट्रैंड

24

एकल क्वेरी में ऐसा करने का कोई तरीका नहीं है। आपको पहले प्रश्न में दस्तावेज़ खोजना होगा:

यदि दस्तावेज़ मौजूद है:

db.bar.update( {user_id : 123456 , "items.item_name" : "my_item_two" } , 
                {$inc : {"items.$.price" : 1} } , 
                false , 
                true);

अन्य

db.bar.update( {user_id : 123456 } , 
                {$addToSet : {"items" : {'item_name' : "my_item_two" , 'price' : 1 }} } ,
                false , 
                true);

शर्त जोड़ने की जरूरत नहीं {$ne : "my_item_two" }

मल्टीथ्रेडेड एनवायरमेंट में भी आपको सावधान रहना होगा कि एक समय में केवल एक धागा ही दूसरे (इंसर्ट केस, अगर डॉक्यूमेंट नहीं मिला हो) को निष्पादित कर सकता है, अन्यथा डुप्लिकेट एंबेड डॉक्युमेंट्स डाले जाएंगे।


1
नहीं, एक ही क्वेरी में ऐसा करने का एक तरीका है, ऊपर देखें
Martijn Scheffer

1
@MartijnScheffer, मुझे इस थ्रेड में कहीं भी एक एकल क्वेरी के रूप में करने का कोई तरीका नहीं दिखता है। यहां तक ​​कि "उत्तर" 2 प्रश्नों का उपयोग कर रहा है जैसे कि इस उत्तर में। क्या मैं कुछ भूल रहा हूँ? मैं यह भी उम्मीद कर रहा हूं कि किसी भी बहु सूत्रण मुद्दों को रोकने के लिए एक क्वेरी में ऐसा करने का एक तरीका है
dferraro

@ यूटिट, मैं वस्तुओं का उपयोग कैसे कर सकता हूं। शर्त के अंदर $ मूल्य। जब मैं एक शर्त जोड़ने की कोशिश करता हूं जैसे "वेतन वृद्धि केवल अगर कीमत 0 से अधिक है", तो यह काम नहीं करता है - मूल रूप से कुछ भी अपडेट नहीं होता है। क्या मैं इसका उपयोग उस स्थिति में कर सकता हूं, या मैं कुछ याद कर रहा हूं? आइटम। $। मूल्य: {$ gt: 0}
dferraro

कुछ गायब हो गया होगा :)
Martijn Scheffer

3

हम $setऑब्जेक्ट के अंदर नेस्टेड एरे को अपडेट करने के लिए ऑपरेटर का उपयोग कर सकते हैं

db.getCollection('geolocations').update( 
   {
       "_id" : ObjectId("5bd3013ac714ea4959f80115"), 
       "geolocation.country" : "United States of America"
   }, 
   { $set: 
       {
           "geolocation.$.country" : "USA"
       } 
    }, 
   false,
   true
);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.