如何防止Mongoose的updateOne使用$set时修改内嵌数组元素的_id
问题原因
你当前的写法是用characters.$直接整体替换匹配到的内嵌角色文档,由于你传入的characterObj不包含原有的_id字段,而Mongoose默认会为子文档自动生成_id,因此更新后会出现新的ObjectId。
可行解决方案
方案1:合并原有_id到更新对象
改动最小,不需要调整Schema或遍历字段,直接把查询用的characterId合并到更新对象即可:
const characterObj = { age: "26", name: "Dr. Raghu", }; // 合并原有_id,避免生成新_id const updateChar = { ...characterObj, _id: characterId }; Drama.updateOne( { _id: req.drama._id, "characters._id": characterId }, { $set: { "characters.$": updateChar } }, function(err, foundlist) { if (err) console.log(err); else console.log("Update completed"); } );
注意:该方案为整体替换子文档,如果原角色文档还有其他未出现在characterObj的字段,会被删除,仅适合需要全量覆盖子文档内容的场景
方案2:动态生成更新字段
无需手动拼接每个字段,遍历characterObj的属性自动生成带前缀的更新规则,仅更新指定字段,不会触碰_id也不会删除其他未指定的字段:
// 自动生成所有要更新的字段,前缀加上数组定位符 const updateFields = Object.fromEntries( Object.entries(characterObj).map(([key, val]) => [`characters.$.${key}`, val]) ); Drama.updateOne( { _id: req.drama._id, "characters._id": characterId }, { $set: updateFields }, function(err, foundlist) { if (err) console.log(err); else console.log("Update completed"); } );
该方案适合字段数量多、经常调整更新字段的场景,不需要每次手动修改$set内的配置。
方案3:关闭子文档自动生成_id的配置
如果你的业务场景里不需要内嵌角色文档的_id字段,可以直接在定义Drama模型的Schema时,关闭子文档的自动_id生成,从底层解决问题:
// 定义角色子Schema时关闭_id const characterSchema = new Schema({ age: String, name: String }, { _id: false }); // Drama的Schema中引用这个子Schema const dramaSchema = new Schema({ characters: [characterSchema], // 其他字段... });
配置完成后再用原写法整体替换子文档,也不会生成新的_id字段,后续所有子文档操作都不需要再处理_id问题。
内容的提问来源于stack exchange,提问作者Deepanshu
相关产品推荐
相关产品推荐

