我想更新一个文档的 _id
字段。我知道这不是一个很好的做法。但由于某种技术原因,我需要更新它。
如果我尝试更新它,我会得到:
db.clients.update({ _id: ObjectId("123")}, { $set: { _id: ObjectId("456")}})
Performing an update on the path '_id' would modify the immutable field '_id'
并且更新被拒绝。我怎样才能更新它?
你不能更新它。您必须使用新的 _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")})
要为整个集合执行此操作,您还可以使用循环(基于 Niels 示例):
db.status.find().forEach(function(doc){
doc._id=doc.UserId; db.status_new.insert(doc);
});
db.status_new.renameCollection("status", true);
在这种情况下,UserId 是我想使用的新 ID
list()
是合乎逻辑的,但对于大型数据库,这可能会耗尽内存
如果您想在同一个集合中重命名 _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});
}
});
if (doc._id.indexOf("2019:") != 0) {... 需要防止无限循环,因为 forEach 会选择插入的文档,即使使用了 .snapshot() 方法。
find(...).snapshot is not a function
但除此之外,很好的解决方案。此外,如果您想用您的自定义 ID 替换 _id
,您可以检查 doc._id.toString().length === 24
以防止无限循环(假设您的自定义 ID 也不是 24 characters long),
在这里,我有一个解决方案,可以避免多个请求、for 循环和旧文档删除。
您可以轻松地手动创建一个新想法,例如:_id:ObjectId()
但是如果知道 Mongo 会在缺少 _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 生成另一个值。
您还可以从 MongoDB 指南针或使用命令创建新文档并设置所需的 specific _id
值。
作为对上述答案的一个非常小的改进,我建议使用
let doc1 = {... doc};
然后
db.dyn_user_metricFormulaDefinitions.deleteOne({_id: doc._id});
这样我们就不需要创建额外的变量来保存旧的_id。
稍微修改了上面@Florent Arlandis 的示例,我们从文档中的不同字段插入_id:
> 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" }
不要像@Florent Arlandis 的回答那样使用 $match
过滤器 + $out
,因为 $out 在插入聚合结果之前会完全删除 collection 中的数据,因此您将有效地丢失所有与 $match
过滤器不匹配的数据
不定期副业成功案例分享
insert
行中获得duplicate key error
并且不担心@skelly 提到的问题,最简单的解决方案是先调用remove
行,然后调用insert
行。doc
应该已经打印在您的屏幕上,因此对于简单的文档,即使插入失败,在最坏的情况下也很容易恢复。