मैं MongoDB में किसी वस्तु को आंशिक रूप से कैसे अपडेट करूं, ताकि नई वस्तु मौजूदा के साथ ओवरले / मर्ज हो जाए


183

MongoDB में सहेजे गए इस दस्तावेज़ को देखते हुए

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
   }
}

पर नई जानकारी के साथ एक वस्तु param2और param3बाहर की दुनिया से बचाया जा करने की जरूरत है

var new_info = {
    param2 : "val2_new",
    param3 : "val3_new"
};

मैं ऑब्जेक्ट की मौजूदा स्थिति पर नए फ़ील्ड्स को मर्ज / ओवरले करना चाहता हूं ताकि param1 हटाए न जाएं

यह कर रहा हूं

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } } 

MongoDB को आगे ले जाएगा जैसा कि पूछा गया था, वैसा ही कर रहा है, और some_key को उस मान पर सेट करता है। पुराने की जगह।

{
   _id : ...,
   some_key: { 
      param2 : "val2_new",
      param3 : "val3_new"
   }
}

केवल नए फ़ील्ड्स (बिना उन्हें स्पष्ट रूप से बताए) के लिए MongoDB अपडेट करने का तरीका क्या है? इसे पाने के लिए:

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2_new",
        param3 : "val3_new"
   }
}

मैं जावा क्लाइंट का उपयोग कर रहा हूं, लेकिन किसी भी उदाहरण की सराहना की जाएगी


2
कृपया $ मर्ज मुद्दे के लिए वोट करें: jira.mongodb.org/browse/SERVER-21094
अली

जवाबों:


107

यदि मैं प्रश्न को सही ढंग से समझता हूं, तो आप किसी दस्तावेज़ को किसी अन्य दस्तावेज़ की सामग्री के साथ अद्यतन करना चाहते हैं, लेकिन केवल वे फ़ील्ड जो पहले से मौजूद नहीं हैं, और पहले से सेट किए गए फ़ील्ड (भले ही दूसरे मान तक) को पूरी तरह से अनदेखा कर दें।

एकल आदेश में ऐसा करने का कोई तरीका नहीं है।

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


आपके प्रश्न का एक और पढ़ना यह होगा कि आप इससे खुश हैं $set, लेकिन सभी क्षेत्रों को स्पष्ट रूप से सेट नहीं करना चाहते हैं। फिर आप डेटा में कैसे पास होंगे?

आप जानते हैं कि आप निम्न कार्य कर सकते हैं:

db.collection.update(  { _id:...} , { $set: someObjectWithNewData } 

यदि आप सभी क्षेत्रों को बिना शर्त के सेट करना चाहते हैं, तो आप $ मल्टी पैरामीटर का उपयोग कर सकते हैं , जैसे: db.collection.update( { _id:...} , { $set: someObjectWithNewData, { $multi: true } } )
ग्रामीण

12
कोई $ मल्टी पैरामीटर नहीं है। एक बहु विकल्प है (जो आप से जुड़ा हुआ है), लेकिन यह प्रभावित नहीं करता है कि फ़ील्ड कैसे अपडेट किए जाते हैं। यह नियंत्रित करता है कि आप किसी एकल दस्तावेज़ को अपडेट करते हैं, या क्वेरी पैरामीटर से मेल खाने वाले सभी दस्तावेज़।
बॉब कर्न्स

1
इससे कोई मतलब नहीं है। मानगो से निपटने के दौरान परमाणु संचालन के अद्यतन के लिए भी इसे प्राथमिकता दी जाती है। जब भी कोई दूसरा आपका डेटा बदलता है, तो उसे पढ़ने से पहले एक गंदा रीड होता है। और चूंकि मोंगो का कोई लेन-देन नहीं है, इसलिए यह आपके लिए डेटा को खुशी से दूषित करेगा।
मेंढकवास

@froginvasion: आप पुराने मानों को अपडेट पर मिलान फ़िल्टर के रूप में उपयोग करके इसे परमाणु बना सकते हैं। इस तरह, यदि इस बीच एक परस्पर विरोधी समवर्ती अद्यतन हुआ तो आपका अपडेट विफल हो जाएगा। फिर आप इसका पता लगा सकते हैं और उसके अनुसार कार्य कर सकते हैं। इसे आशावादी लॉकिंग कहा जाता है। लेकिन हाँ, यह आपके आवेदन पर इसे ठीक से लागू करने पर निर्भर करता है, मानगो में स्वयं अंतर्निहित लेन-देन नहीं है।
थिलो

मुझे लगता है कि यह दो वस्तुओं को जावास्क्रिप्ट में विलय करने का एक अधिक सामान्य प्रश्न है। IIRC विचार बस करने के लिए पुराने नया एक के गुणों को आवंटित करने के लिए है
information_interchange

110

मैंने इसे अपने फंक्शन से हल किया। यदि आप दस्तावेज़ में निर्दिष्ट फ़ील्ड को अपडेट करना चाहते हैं तो आपको इसे स्पष्ट रूप से संबोधित करने की आवश्यकता है।

उदाहरण:

{
    _id : ...,
    some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
    }
}

यदि आप केवल param2 को अपडेट करना चाहते हैं, तो यह करना गलत है:

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } }  //WRONG

तुम्हें अवश्य उपयोग करना चाहिए:

db.collection.update(  { _id:...} , { $set: { some_key.param2 : new_info  } } 

इसलिए मैंने एक फंक्शन कुछ इस तरह लिखा:

function _update($id, $data, $options=array()){

    $temp = array();
    foreach($data as $key => $value)
    {
        $temp["some_key.".$key] = $value;
    } 

    $collection->update(
        array('_id' => $id),
        array('$set' => $temp)
    );

}

_update('1', array('param2' => 'some data'));

11
यह स्वीकृत उत्तर होना चाहिए। एक बेहतर फ्लैटनऑबजेक्ट फ़ंक्शन के लिए, gist.github.com/penguinboy/762197
steph643

धन्यवाद आपकी प्रतिक्रिया ने मेरा मार्गदर्शन करने में मदद की। मुझे उपयोगकर्ता संग्रह में एक उपयोगकर्ता की विशिष्ट कुंजी के मूल्य को संशोधित करने के लिए एबल्ड किया गया था:db.users.update( { _id: ObjectId("594dbc3186bb5c84442949b1") }, { $set: { resume: { url: "http://i.imgur.com/UFX0yXs.jpg" } } } )
ल्यूक स्कोन

क्या यह ऑपरेशन परमाणु और पृथक है? मतलब, अगर 2 समानांतर धागे एक ही दस्तावेज़ के 2 अलग-अलग क्षेत्रों को सेट करने की कोशिश करते हैं, तो क्या इसका परिणाम रेस की स्थिति और अप्रत्याशित परिणाम होगा
इसलिए-यादृच्छिक-ड्यूड

21

आप उन वस्तुओं के अन्य गुणों को प्रभावित किए बिना, ऑब्जेक्ट के अंदर गहरे क्षेत्रों तक पहुंचने और सेट करने के लिए डॉट-नोटेशन का उपयोग कर सकते हैं।

आपके द्वारा ऊपर दी गई वस्तु को देखते हुए:

> db.test.insert({"id": "test_object", "some_key": {"param1": "val1", "param2": "val2", "param3": "val3"}})
WriteResult({ "nInserted" : 1 })

हम अभी some_key.param2और अपडेट कर सकते हैं some_key.param3:

> db.test.findAndModify({
... query: {"id": "test_object"},
... update: {"$set": {"some_key.param2": "val2_new", "some_key.param3": "val3_new"}},
... new: true
... })
{
    "_id" : ObjectId("56476e04e5f19d86ece5b81d"),
    "id" : "test_object",
    "some_key" : {
        "param1" : "val1",
        "param2" : "val2_new",
        "param3" : "val3_new"
    }
}

आप जितना चाहें उतनी गहरी तल्लीन कर सकते हैं। यह मौजूदा वाले को प्रभावित किए बिना किसी ऑब्जेक्ट में नए गुण जोड़ने के लिए भी उपयोगी है।


क्या मैं एक नई कुंजी जोड़ सकता हूं ... जैसे "कुछ कुंजी": {"param1": "val1", "param2": "val2_new", "param3": "val3_new", "param4": "val4_new",}
उर्वशी सोनी

17

सबसे अच्छा उपाय यह है कि आप ऑब्जेक्ट से प्रॉपर्टीज निकालें और उन्हें फ्लैट डॉट-नोटेशन की-वैल्यू पेयर बनाएं। आप इस पुस्तकालय के उदाहरण के लिए उपयोग कर सकते हैं:

https://www.npmjs.com/package/mongo-dot-notation

इसमें वह .flattenफ़ंक्शन होता है जो आपको ऑब्जेक्ट को उन संपत्तियों के फ्लैट सेट में बदलने की अनुमति देता है जो तब $ $ संशोधक को दिए जा सकते हैं, बिना किसी चिंता के कि आपके मौजूदा DB ऑब्जेक्ट की किसी भी संपत्ति को हटा दिया जाएगा / बिना जरूरत के ओवरराइट किया जाएगा।

mongo-dot-notationडॉक्स से लिया गया :

var person = {
  firstName: 'John',
  lastName: 'Doe',
  address: {
    city: 'NY',
    street: 'Eighth Avenu',
    number: 123
  }
};



var instructions = dot.flatten(person)
console.log(instructions);
/* 
{
  $set: {
    'firstName': 'John',
    'lastName': 'Doe',
    'address.city': 'NY',
    'address.street': 'Eighth Avenu',
    'address.number': 123
  }
}
*/

और फिर यह सही चयनकर्ता बनाता है - यह केवल दिए गए गुणों को अपडेट करेगा। संपादित करें: मैं कुछ समय के लिए पुरातत्वविद् बनना पसंद करता हूं;)


10

मानगो आपको एक .सम्मेलन का उपयोग करके नेस्टेड दस्तावेज़ अपडेट करने देता है । एक बार देखिए: मोंगोडब में नेस्टेड दस्तावेजों को अपडेट करना । मर्ज अपडेट के बारे में अतीत का एक और प्रश्न यहाँ दिया गया है, जैसे कि आप जो मुझे ढूंढ रहे हैं, वह यह है: 'दस्तावेज़' के माध्यम से मोंगोबीडी परमाणु अद्यतन


6

मुझे इस तरह से सफलता मिली:

db.collection.update(  { _id:...} , { $set: { 'key.another_key' : new_info  } } );

मेरे पास एक फ़ंक्शन है जो गतिशील रूप से मेरे प्रोफ़ाइल अपडेट को संभालता है

function update(prop, newVal) {
  const str = `profile.${prop}`;
  db.collection.update( { _id:...}, { $set: { [str]: newVal } } );
}

नोट: 'प्रोफ़ाइल' मेरे कार्यान्वयन के लिए विशिष्ट है, यह सिर्फ उस कुंजी का तार है जिसे आप संशोधित करना चाहते हैं।


1

ऐसा लगता है कि आप सेट कर सकते हैं ।PartialObject जो आप चाहते हैं पूरा कर सकते हैं।


इसे आजमाया। अगर मैं गलत नहीं हूँ तो यह आपको आंशिक वस्तुओं को बचाने नहीं देता है ... लेकिन धन्यवाद
एरन मेडन

1
    // where clause DBObject
    DBObject query = new BasicDBObject("_id", new ObjectId(id));

    // modifications to be applied
    DBObject update = new BasicDBObject();

    // set new values
    update.put("$set", new BasicDBObject("param2","value2"));

   // update the document
    collection.update(query, update, true, false); //3rd param->upsertFlag, 4th param->updateMultiFlag

यदि आपके पास अद्यतन करने के लिए कई फ़ील्ड हैं

        Document doc = new Document();
        doc.put("param2","value2");
        doc.put("param3","value3");
        update.put("$set", doc);

1

शुरू करना Mongo 4.2, db.collection.update()एक एकत्रीकरण पाइपलाइन को स्वीकार कर सकता है, जो एकत्रीकरण ऑपरेटरों का उपयोग करने की अनुमति देता है, जैसे $addFieldsकि इनपुट दस्तावेजों और नए जोड़े गए क्षेत्रों से सभी मौजूदा क्षेत्रों को आउटपुट करता है:

var new_info = { param2: "val2_new", param3: "val3_new" }

// { some_key: { param1: "val1", param2: "val2", param3: "val3" } }
// { some_key: { param1: "val1", param2: "val2"                 } }
db.collection.update({}, [{ $addFields: { some_key: new_info } }], { multi: true })
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
  • पहला भाग {}मैच क्वेरी है, जिसे छानना है कि कौन से दस्तावेज अपडेट करने हैं (इस मामले में सभी दस्तावेज)।

  • दूसरा भाग [{ $addFields: { some_key: new_info } }]अद्यतन एकत्रीकरण पाइपलाइन है:

    • एक एकत्रीकरण पाइपलाइन के उपयोग को इंगित करने वाले वर्ग कोष्ठक पर ध्यान दें।
    • चूंकि यह एक एकत्रीकरण पाइपलाइन है, हम उपयोग कर सकते हैं $addFields
    • $addFields ठीक वही करता है जो आपको चाहिए: वस्तु को अपडेट करना ताकि नई वस्तु मौजूदा के साथ ओवरले / मर्ज हो जाए:
    • इस मामले में, { param2: "val2_new", param3: "val3_new" }मौजूदा में मिल जाएँगे some_keyरखकर param1अछूता है और या तो जोड़ सकते हैं या दोनों की जगह param2और param3
  • मत भूलना { multi: true }, अन्यथा केवल पहले मिलान दस्तावेज़ को अपडेट किया जाएगा।


1
db.collection.update(  { _id:...} , { $set: { some_key : new_info  } } 

सेवा

db.collection.update( { _id: ..} , { $set: { some_key: { param1: newValue} } } ); 

उममीद है कि इससे मदद मिलेगी!


1

आप बल्कि एक मुखर कर सकते हैं, MongoDB में इस ऑपरेशन का उपयोग दस्तावेज़ को संग्रह में सहेजने के लिए किया जाता है। यदि दस्तावेज़ क्वेरी मानदंडों से मेल खाता है, तो यह अपडेट ऑपरेशन करेगा अन्यथा यह संग्रह में एक नया दस्तावेज़ सम्मिलित करेगा।

नीचे जैसा कुछ

db.employees.update(
    {type:"FT"},
    {$set:{salary:200000}},
    {upsert : true}
 )

0

$ $ इस प्रक्रिया का उपयोग करें

.update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}})
.exec()
.then(dashboardDoc => {
    return {
        result: dashboardDoc
    };
}); 

0

आवश्यक डॉट पथ सहित संपत्ति नामों के साथ एक अद्यतन ऑब्जेक्ट बनाएं। ("ओपेक।" + ओपी के उदाहरण से), और फिर उस अपडेट का उपयोग करें।

//the modification that's requested
updateReq = { 
   param2 : "val2_new",
   param3 : "val3_new"   
}

//build a renamed version of the update request
var update = {};
for(var field in updateReq){
    update["somekey."+field] = updateReq[field];
}

//apply the update without modifying fields not originally in the update
db.collection.update({._id:...},{$set:update},{upsert:true},function(err,result){...});

0

हाँ, सबसे अच्छा तरीका है ऑब्जेक्ट नोटेशन को एक फ्लैट की-वैल्यू स्ट्रिंग प्रतिनिधित्व में बदलना, जैसा कि इस टिप्पणी में बताया गया है: https://stackoverflow.com/a/39357531/2529199

मैं इस एनपीएम लाइब्रेरी का उपयोग करके एक वैकल्पिक विधि को उजागर करना चाहता था: https://www.npmjs.com/package/dot-object जो आपको डॉट नोटेशन का उपयोग करके विभिन्न वस्तुओं में हेरफेर करने देता है।

मैंने इस पैटर्न का उपयोग प्रोग्राम वेरिएबल के रूप में की-वैल्यू को स्वीकार करते हुए, प्रोग्राम्ड नेस्टेड ऑब्जेक्ट प्रॉपर्टी को बनाने के लिए किया है:

const dot = require('dot-object');

function(docid, varname, varvalue){
  let doc = dot.dot({
      [varname]: varvalue 
  });

  Mongo.update({_id:docid},{$set:doc});
}

यह पैटर्न मुझे नेस्टेड के साथ-साथ एकल-स्तर के गुणों का परस्पर उपयोग करने देता है, और उन्हें मैंगो में सफाई से सम्मिलित करता है।

यदि आपको केवल विशेष रूप से क्लाइंट-साइड पर जेएस ऑब्जेक्ट्स के साथ जेएस ऑब्जेक्ट्स के साथ खेलने की आवश्यकता है, लेकिन मैंगो के साथ काम करते समय निरंतरता है, तो यह लाइब्रेरी आपको पहले बताए गए mongo-dot-notationएनपीएम मॉड्यूल की तुलना में अधिक विकल्प प्रदान करती है ।

पीएस मैं मूल रूप से सिर्फ एक टिप्पणी के रूप में इसका उल्लेख करना चाहता था, लेकिन जाहिर तौर पर मेरा एस / ओ प्रतिनिधि एक टिप्पणी पोस्ट करने के लिए पर्याप्त नहीं है। तो, SzybkiSasza की टिप्पणी पर पेशी की कोशिश नहीं कर रहा था, बस एक वैकल्पिक मॉड्यूल प्रदान करना चाहता था।



0

आपको वस्तु को परस्पर अद्यतन करने के बारे में सोचना चाहिए और फिर वस्तु को अद्यतन फ़ील्ड के साथ संग्रहित करना चाहिए। नीचे जैसा कुछ किया है

function update(_id) {
    return new Promise((resolve, reject) => {

        ObjModel.findOne({_id}).exec((err, obj) => {

            if(err) return reject(err)

            obj = updateObject(obj, { 
                some_key: {
                    param2 : "val2_new",
                    param3 : "val3_new"
                }
            })

            obj.save((err, obj) => {
                if(err) return reject(err)
                resolve(obj)
            })
        })
    })
}


function updateObject(obj, data) {

    let keys = Object.keys(data)

    keys.forEach(key => {

        if(!obj[key]) obj[key] = data[key]

        if(typeof data[key] == 'object') 
            obj[key] = updateObject(obj[key], data[key])
        else
            obj[key] = data[key]
    })

    return obj
}

0

आपको एंबेडेड डॉक्युमेंट्स का उपयोग करना होगा (पथ ऑब्जेक्ट को कड़े करना)

let update = {}
Object.getOwnPropertyNames(new_info).forEach(param => {
   update['some_key.' + param] = new_info[param]
})

और इसलिए, जावास्क्रिप्ट में आप अपडेट करने के लिए स्प्रेड ऑपरेटर (...) का उपयोग कर सकते हैं

db.collection.update(  { _id:...} , { $set: { ...update  } } 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.