如何更新一个MongoDB文档的_id?

shi*_*ara 121 mongodb

我想更新一个文档的_id MongoDB.我知道这不是一个非常好的实践.但由于某些技术原因,我需要更新它.但如果我尝试更新它,我有:

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

Performing an update on the path '_id' would modify the immutable field '_id'
Run Code Online (Sandbox Code Playgroud)

而且没有更新.我怎么能真正更新它?

Nie*_*est 203

你无法更新它.您必须使用新文档保存文档_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")})
Run Code Online (Sandbox Code Playgroud)

  • 如果该文档上的某些字段具有唯一索引,则会出现一个有趣的问题.在这种情况下,您的示例将失败,因为无法在唯一索引字段中插入具有重复值的文档.您可以通过先执行删除来解决此问题,但这是一个坏主意,因为如果您的插入由于某种原因失败,您的数据现在会丢失.您必须删除索引,执行工作,然后恢复索引. (28认同)
  • 如果您在“insert”行中收到“duplicate key error”并且不担心@skelly提到的问题,最简单的解决方案是先调用“remove”行,然后调用“insert”行。对于简单的文档,“doc”应该已经打印在屏幕上,这样在最坏的情况下,即使插入失败,也很容易恢复。 (2认同)
  • @ShankhadeepGhoshal 是的,这是一个风险,特别是如果您针对实时生产系统执行此操作。不幸的是,我认为最好的选择是计划中断并在此过程中停止所有编写器。另一个可能不那么痛苦的选择是强制应用程序暂时进入只读模式。将写入数据库的所有应用程序重新配置为仅指向辅助节点。在此期间读取将成功,但写入将失败,并且您的数据库将保持静态。 (2认同)

Pat*_*olf 30

要为整个集合执行此操作,您还可以使用循环(基于Niels示例):

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

在这种情况下,UserId是我想要使用的新ID

  • 呃,这有11个赞成,但是有人说这是一个无限循环?这是什么交易? (4认同)
  • @Andrew因为当代流行编码器文化要求你在实际验证所述输入确实有效之前应该总是承认好的输入. (4认同)

小智 5

如果您想在同一集合中重命名 _id(例如,如果您想为某些 _id 加上前缀):

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});
   }
});
Run Code Online (Sandbox Code Playgroud)

if (doc._id.indexOf("2019:") != 0) {...需要防止无限循环,因为 forEach 选择插入的文档,甚至通过使用.snapshot()方法。