Reputation: 46914
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?
Upvotes: 194
Views: 187673
Reputation: 3727
To generate new id for one document.
function createNewId(collectionDb,id) {
let obj = collectionDb.find({"_id":ObjectId(id)}).toArray()[0];
collectionDb.deleteOne({"_id":obj._id});
obj._id =null;
collectionDb.insertOne(obj);
}
createNewId(db.justSomeCollection,"678f8fe70be258cca392a641")
After this operation, we have a new id number assigned from the mongo database.
Upvotes: 0
Reputation: 4078
You could try using aggregation with an $out
stage, leaving all ids untouched except for the one you want to modify.
db.clients.aggregate([
{$addFields: {
_id: {$function: {
body: function(id) {
// not sure if this exact condition will work but you get the gist
return id === ObjectId("123") ? ObjectId("456") : id;
},
args: ["$_id"],
lang: "js"
}}
}},
{$out: "clients"}
]);
Upvotes: 0
Reputation: 4857
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 lose all data that don't match to $match
filter
Upvotes: 0
Reputation: 843
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.
Upvotes: 0
Reputation: 854
You can also create a new document from MongoDB compass or using command and set the specific _id
value that you want.
Upvotes: 1
Reputation: 925
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'}
])
Upvotes: 1
Reputation: 61
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.
Upvotes: 6
Reputation: 2569
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
Upvotes: 42
Reputation: 32204
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")})
Upvotes: 302