如何更新MongoDB文档的_id?

idfiyjo8  于 2023-06-22  发布在  Go
关注(0)|答案(8)|浏览(217)

我想更新一个文档的_id字段。我知道这不是很好的练习。但由于一些技术原因,我需要更新它。
如果我尝试更新它,我会得到:

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

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

并且更新被拒绝。我如何更新它?

nnvyjq4y

nnvyjq4y1#

您无法更新它。您必须使用新的_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")})
6za6bjd0

6za6bjd02#

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

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

在本例中,UserId是我想使用的新ID

y4ekin9u

y4ekin9u3#

如果你想在同一个集合中重命名_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() 方法。
c9x0cxw0

c9x0cxw04#

这里我有一个解决方案,可以避免多个请求,for循环和旧文档删除。
您可以轻松地创建一个新的想法手动使用类似的东西:_id:ObjectId()但是知道Mongo会在缺少_id的情况下自动分配一个_id,您可以使用aggregate创建一个包含文档中所有字段的$project,但省略field _id。然后可以使用$out保存它
如果你的文件是:

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

然后你的查询将是:

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

ojsjcaue5#

您也可以从MongoDB compass或使用命令创建一个新文档,并设置您想要的specific _id值。

t9eec4r0

t9eec4r06#

作为对上述答案的一个很小的改进,我建议使用

let doc1 = {... doc};

然后

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

这样我们就不需要创建额外的变量来保存old _id。

55ooxyrt

55ooxyrt7#

稍微修改了上面@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 filter + $out,因为$out在插入聚合结果之前会完全删除集合中的数据,所以实际上你会丢失所有与$match filter不匹配的数据

mec1mxoz

mec1mxoz8#

您可以尝试在$out stage中使用聚合,除了要修改的id之外,其他所有id都保持不变。

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"}
]);

相关问题