कैसे एक MongoDB दस्तावेज़ के _id को अपडेट करें?


129

मैं _idएक दस्तावेज़ के एक क्षेत्र को अद्यतन करना चाहता हूं । मुझे पता है कि यह वास्तव में अच्छा pratice नहीं है। लेकिन कुछ तकनीकी कारण से, मुझे इसे अपडेट करने की आवश्यकता है। अगर मैं इसे अपडेट करने की कोशिश करूँ तो मुझे यह मिलेगा:

> db.clients.update({ _id: ObjectId("123")}, { $set: { _id: ObjectId("456")}})

Performing an update on the path '_id' would modify the immutable field '_id'

और अद्यतन नहीं किया गया है। मैं इसे कैसे अपडेट कर सकता हूं?

जवाबों:


216

आप इसे अपडेट नहीं कर सकते। आपको एक नए का उपयोग करके दस्तावेज़ को सहेजना होगा _id, और फिर पुराने दस्तावेज़ को निकालना होगा।

// store the document in a variable
doc = db.clients.findOne({_id: ObjectId("4cc45467c55f4d2d2a000002")})

// set a new _id on the document
doc._id = ObjectId("4c8a331bda76c559ef000004")

// insert the document, using the new _id
db.clients.insert(doc)

// remove the document with the old _id
db.clients.remove({_id: ObjectId("4cc45467c55f4d2d2a000002")})

29
इसके साथ एक मजेदार मुद्दा यह प्रतीत होता है कि उस दस्तावेज़ के कुछ क्षेत्र में एक अद्वितीय सूचकांक है। उस स्थिति में, आपका उदाहरण विफल हो जाएगा क्योंकि एक अद्वितीय अनुक्रमित फ़ील्ड में डुप्लिकेट मान के साथ कोई दस्तावेज़ नहीं डाला जा सकता है। आप इसे पहले डिलीट करके ठीक कर सकते हैं, लेकिन यह एक बुरा विचार है क्योंकि यदि आपका इंसर्ट किसी कारण से विफल हो जाता है तो आपका डेटा अब खो गया है। आपको इसके बजाय अपने सूचकांक को छोड़ना होगा, कार्य करना होगा, फिर सूचकांक को पुनर्स्थापित करना होगा।
स्केली

अच्छी बात @skelly! मैं ऐसी ही समस्याओं के बारे में सोच रहा था और 2 घंटे पहले आपकी ताज़ा टिप्पणी देखी। तो इस संशोधित आईडी झंझट को एक आंतरिक समस्या के रूप में माना जाता है जो उपयोगकर्ता को आईडी चुनने की अनुमति देता है?
रायलू

1
यदि आप एक मिलता है duplicate key errorमें insertरेखा और समस्या @skelly उल्लेख के बारे में चिंतित नहीं हैं, सबसे आसान समाधान सिर्फ कॉल करने के लिए है removeपहली पंक्ति और फिर फोन insertलाइन। docपहले से ही अपनी स्क्रीन पर मुद्रित करना होगा कि वह ठीक करने के लिए, सबसे खराब स्थिति है, भले ही डालने में विफल रहता है, सरल दस्तावेज़ों के लिए आसान होगा।
फिल्पेरो

पैरामीटर के रूप में स्ट्रिंग के बिना सिर्फ ऑब्जेक्टआईड () का उपयोग करना एक नया अनूठा उत्पन्न करेगा।
एरिक

1
@ शशांकदीप घोषाल यस एक जोखिम है, खासकर अगर आप लाइव उत्पादन प्रणाली के खिलाफ यह प्रदर्शन कर रहे हैं। दुर्भाग्य से मुझे लगता है कि इस प्रक्रिया के दौरान अनुसूचित लेखकों को रोकना और सभी लेखकों को रोकना सबसे अच्छा विकल्प है। एक और विकल्प जो थोड़ा कम दर्दनाक हो सकता है वह अनुप्रयोगों को अस्थायी रूप से केवल-पढ़ने के लिए मोड में लागू करने के लिए होगा। सभी ऐप्स को फिर से कॉन्फ़िगर करें जो केवल एक माध्यमिक नोड को इंगित करने के लिए डीबी लिखते हैं। रीड्स सफल होंगे, लेकिन इस समय के दौरान लिखना विफल हो जाएगा, और आपका DB स्थिर रहेगा।
21

32

अपने पूरे संग्रह के लिए ऐसा करने के लिए आप एक लूप (नील्स उदाहरण के आधार पर) का उपयोग कर सकते हैं:

db.status.find().forEach(function(doc){ 
    doc._id=doc.UserId; db.status_new.insert(doc);
});
db.status_new.renameCollection("status", true);

इस मामले में UserId नई आईडी थी जिसका मैं उपयोग करना चाहता था


1
क्या स्नैपशॉट () खोजने पर सलाह दी जाएगी कि गलती से नए डॉक्स लेने से पहले ही इसे हटा दें?
जॉन फ्लिनकॉफ़

यह कोड स्निपेट कभी पूरा नहीं होता है। यह हमेशा के लिए संग्रह पर और अधिक पुनरावृति करता रहता है। स्नैपशॉट वह नहीं करता है जो आप अपेक्षा करते हैं (आप संग्रह में एक दस्तावेज़ जोड़कर 'स्नैपशॉट' ले कर उसका परीक्षण कर सकते हैं, फिर यह देखते हुए कि नया दस्तावेज़ स्नैपशॉट में है)
पैट्रिक

स्नैपशॉट के विकल्प के लिए stackoverflow.com/a/28083980/305324 देखें । list()तार्किक एक है, लेकिन बड़े डेटाबेस के लिए यह मेमोरी समाप्त हो सकती है
पैट्रिक

4
उह, इसमें 11 उत्थान हैं लेकिन कोई कहता है कि यह एक अनंत लूप है? यहाँ क्या सौदा है?
एंड्रयू

4
@ और इसलिए क्योंकि समकालीन पॉप-कोडर संस्कृति यह बताती है कि आपको वास्तव में अच्छे इनपुट को स्वीकार करने से पहले वास्तव में सत्यापित करना चाहिए कि इनपुट वास्तव में काम करता है।
सीएसवन

5

मामले में, आप उसी संग्रह में _id का नाम बदलना चाहते हैं (उदाहरण के लिए, यदि आप कुछ _ids को प्रीफ़िक्स करना चाहते हैं):

db.someCollection.find().snapshot().forEach(function(doc) { 
   if (doc._id.indexOf("2019:") != 0) {
       print("Processing: " + doc._id);
       var oldDocId = doc._id;
       doc._id = "2019:" + doc._id; 
       db.someCollection.insert(doc);
       db.someCollection.remove({_id: oldDocId});
   }
});

अगर (doc._id.indexOf ("2019:")! = 0) {... अनंत लूप को रोकने के लिए आवश्यक है, क्योंकि सम्मिलित डॉक्स को निकालता है , यहां तक ​​कि थ्रू .snapshot () विधि का उपयोग किया जाता है।


find(...).snapshot is not a functionलेकिन इसके अलावा, महान समाधान। इसके अलावा, यदि आप _idअपनी कस्टम आईडी से बदलना चाहते हैं, तो आप यह देख सकते हैं doc._id.toString().length === 24कि क्या अनंत लूप को रोकने के लिए (आपकी कस्टम आईडी भी 24 वर्ण लंबी नहीं है ),
दान Dascalescu

1

यहां मेरे पास एक समाधान है जो लूप और पुराने दस्तावेज़ को हटाने के लिए कई अनुरोधों से बचता है।

आप आसानी से कुछ का उपयोग करके मैन्युअल रूप से एक नया विचार बना सकते हैं: _id:ObjectId() लेकिन मानगो को जानने के बाद स्वचालित रूप से एक _id असाइन हो जाएगा, यदि आप $projectअपने दस्तावेज़ के सभी फ़ील्ड बनाने के लिए कुल का उपयोग कर सकते हैं , लेकिन फ़ील्ड _id को छोड़ दें। इसके बाद आप इसे सहेज सकते हैं$out

तो अगर आपका दस्तावेज़ है:

{
"_id":ObjectId("5b5ed345cfbce6787588e480"),
"title": "foo",
"description": "bar"
}

तब आपकी क्वेरी होगी:

    db.getCollection('myCollection').aggregate([
        {$match:
             {_id: ObjectId("5b5ed345cfbce6787588e480")}
        }        
        {$project:
            {
             title: '$title',
             description: '$description'             
            }     
        },
        {$out: 'myCollection'}
    ])

दिलचस्प विचार ... लेकिन आप आम तौर पर _idएक दिए गए मूल्य को सेट करना चाहते हैं , बजाय इसके कि MongoDB एक और उत्पन्न करें।
दान डैस्कलेस्कु

0

आप MongoDB कम्पास या कमांड का उपयोग करके एक नया दस्तावेज़ भी बना सकते हैं और वह specific _idमान सेट कर सकते हैं जो आप चाहते हैं।

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