开发者

How to update the _id of one MongoDB Document?

开发者 https://www.devze.com 2023-01-22 07:34 出处:网络
I want update an _id field of one document. I know it\'s not really good practice. But for some technical reason, I need to update it.

I want update an _id field of one document. I know it's not really good practice. But for some technical reason, I need to update it.

If I try to update it I get:

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

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

And the update is rejected开发者_开发百科. How I can update it?


You cannot update it. You'll have to save the document using a new _id, and then remove the old document.

// 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")})


To do it for your whole collection you can also use a loop (based on Niels example):

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

In this case UserId was the new ID I wanted to use


In case, you want to rename _id in same collection (for instance, if you want to prefix some _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});
   }
});

if (doc._id.indexOf("2019:") != 0) {... needed to prevent infinite loop, since forEach picks the inserted docs, even throught .snapshot() method used.


Here I have a solution that avoid multiple requests, for loops and old document removal.

You can easily create a new idea manually using something like:_id:ObjectId() But knowing Mongo will automatically assign an _id if missing, you can use aggregate to create a $project containing all the fields of your document, but omit the field _id. You can then save it with $out

So if your document is:

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

Then your query will be:

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


You can also create a new document from MongoDB compass or using command and set the specific _id value that you want.


As a very small improvement to the above answers i would suggest using

let doc1 = {... doc};

then

db.dyn_user_metricFormulaDefinitions.deleteOne({_id: doc._id});

This way we don't need to create extra variable to hold old _id.


Slightly modified example of @Florent Arlandis above where we insert _id from a different field in a document:

 > db.coll.insertOne({ "_id": 1, "item": { "product": { "id": 11 } },   "source": "Good Store" })
 { "acknowledged" : true, "insertedId" : 1 }
 > db.coll.aggregate( [ { $set: { _id : "$item.product.id" }}, { $out: "coll" } ]) // inserting _id you want for the current collection
 > db.coll.find() // check that _id is changed
 { "_id" : 11, "item" : { "product" : { "id" : 11 } }, "source" : "Good Store" }

Do not use $match filter + $out as in @Florent Arlandis's answer since $out fully remove data in collection before inserting aggregate result, so effectively you will loose all data that don't match to $match filter

0

精彩评论

暂无评论...
验证码 换一张
取 消