You need to enable JavaScript to run this app. 使用findOneAndUpdate更新两个不同ObjectId用户数组时仅单条更新生效
优惠活动
大模型
产品
解决方案
定价
更多

使用findOneAndUpdate更新两个不同ObjectId用户数组时仅单条更新生效

问题根因定位

你现在遇到的双写仅单条成功的问题,是三个高频开发坑叠在一起导致的:

  • 核心错误:findOneAndUpdate的第一个参数必须是查询过滤规则对象,用来指定要匹配哪条文档。你现在直接把接口拿到的用户ID字符串当第一个参数传,新版本的MongoDB驱动、Mongoose根本不会隐式把这个字符串当成匹配_id字段的条件——相当于查询规则完全失效,要么匹配不到任何文档,触发的报错被异步回调静默吞掉,要么靠upsert建了个完全不符合预期的脏文档,根本没写到你要找的那两个用户对象上。
  • 执行链路不可靠:你现在用纯回调的方式写两个异步操作,既没有捕获每一步的报错,也没有做流程串联。只要其中某一步抛异常,后续操作根本不会正常执行,自然就出现只成功一个的现象。
  • 没有原子性保障:两个独立的更新操作之间没有兜底机制,一旦执行到一半出现网络波动、数据库连接中断,就会出现某一个用户的数组更新成功、另一个没更新成功的脏数据问题。
修正实现方案

首先必须先把查询参数的错误改对,这是能跑通的基础:

// 你的原代码(错误写法)
Users.findOneAndUpdate(userToFollow, { $push: ... })
// 正确写法:明确指定匹配规则,比如按用户的_id字段匹配目标文档
Users.findOneAndUpdate({ _id: userToFollow }, { $push: ... })

改完传参问题后,再用async/await把两个操作的执行链路串起来,加上统一错误捕获,保证每一步的执行结果都能被感知:

// 接口处理函数示例
exports.follow = async (req, res) => {
  try {
    const userToFollow = req.body.following;
    const userThatFollows = req.body.follower;

    // 顺序执行两个更新,每一步完成后再执行下一步,抛错就直接进入catch分支
    await Users.findOneAndUpdate(
      { _id: userToFollow },
      { $push: { followers: userThatFollows } },
      { new: true, upsert: true }
    );

    await Users.findOneAndUpdate(
      { _id: userThatFollows },
      { $push: { following: userToFollow } },
      { new: true, upsert: true }
    );

    res.json({ code: 200, message: "关注关系写入成功" });
  } catch (err) {
    // 所有环节的报错都会在这里被捕获,不会被静默吞掉
    res.status(500).json({ code: 500, message: "关注关系写入失败", detail: err.message });
  }
}

可选优化

  • 避免数组重复数据:如果不想出现同一个用户被重复push到followers/following数组里,可以把$push换成$addToSet,从根源上防止重复值。
  • 原子性保障:如果你的MongoDB是4.0及以上版本,推荐用多文档事务把两个更新操作包成原子流程,不管哪一步执行失败,都会回滚到操作前的状态,彻底避免数据不一致的问题:
const session = await mongoose.startSession();
try {
  session.startTransaction();
  // 两个更新操作都要传入当前session标识
  await Users.findOneAndUpdate(
    { _id: userToFollow },
    { $addToSet: { followers: userThatFollows } },
    { new: true, upsert: true, session }
  );
  await Users.findOneAndUpdate(
    { _id: userThatFollows },
    { $addToSet: { following: userToFollow } },
    { new: true, upsert: true, session }
  );
  await session.commitTransaction();
} catch (e) {
  // 任意一步失败就回滚所有操作
  await session.abortTransaction();
  // 错误处理逻辑
} finally {
  // 最后关闭session释放资源
  await session.endSession();
}

内容的提问来源于stack exchange,提问作者Arham Farman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 22:15:04