You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 14:06:09