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

Mongoose如何实现对象数组元素存在即更新,替代直接追加操作?

实现方案

核心逻辑是先通过唯一标识判断数组中是否存在目标元素,存在则更新对应元素,不存在则追加新元素。我们默认以文档自身_id作为重复判断依据,你可以根据业务需求替换为fileid等其他唯一字段。


方案一:聚合管道更新(推荐)

适用于MongoDB 4.2及以上版本,属于原子操作,无并发更新异常问题,代码改动最小:

exports.pushDocumentsList = (req, res) => {
  // 构造待更新的文档对象
  const targetDoc = {
    _id: req.body.document._id,
    fileid: req.body.document.fileid,
    filename: req.body.document.filename,
    url: req.body.document.url ?? '',
    status: req.body.document.status ?? '',
    expiredate: req.body.document.expiredate ?? '',
  };

  User.findOneAndUpdate(
    { _id: req.profile._id },
    // 传入聚合管道实现动态更新逻辑
    [{
      $set: {
        documents: {
          $cond: {
            // 第一步:判断数组中是否存在相同_id的元素
            if: { $in: [targetDoc._id, "$documents._id"] },
            // 存在:遍历数组更新匹配到的元素
            then: {
              $map: {
                input: "$documents",
                in: {
                  $cond: {
                    if: { $eq: ["$$this._id", targetDoc._id] },
                    then: targetDoc,
                    else: "$$this"
                  }
                }
              }
            },
            // 不存在:追加新元素到数组末尾
            else: { $concatArrays: ["$documents", [targetDoc]] }
          }
        }
      }
    }],
    { new: true, useFindAndModify: false },
    (err, docu) => {
      if (err) {
        return res.status(400).json({
          error: "Non è stato possibile aggiornare i documenti dell'utente",
        });
      }
      return res.status(200).json({
        status: 'success',
        message: 'Documento caricato con successo',
        doc: req.body.document,
      });
    }
  );
};

方案二:低版本MongoDB兼容方案

如果MongoDB版本低于4.2不支持聚合管道更新,可以用先查询判断、再分场景操作的方式实现:

exports.pushDocumentsList = (req, res) => {
  const targetDoc = {
    _id: req.body.document._id,
    fileid: req.body.document.fileid,
    filename: req.body.document.filename,
    url: req.body.document.url ?? '',
    status: req.body.document.status ?? '',
    expiredate: req.body.document.expiredate ?? '',
  };

  // 先查询是否存在匹配的元素
  User.findOne({
    _id: req.profile._id,
    "documents._id": targetDoc._id
  }, (err, existsUser) => {
    if (err) {
      return res.status(400).json({
        error: "Non è stato possibile aggiornare i documenti dell'utente",
      });
    }

    if (existsUser) {
      // 元素存在,更新对应位置的元素
      User.findOneAndUpdate(
        { _id: req.profile._id, "documents._id": targetDoc._id },
        { $set: { "documents.$": targetDoc } },
        { new: true, useFindAndModify: false },
        (updateErr) => {
          if (updateErr) {
            return res.status(400).json({
              error: "Non è stato possibile aggiornare i documenti dell'utente",
            });
          }
          return res.status(200).json({
            status: 'success',
            message: 'Documento aggiornato con successo',
            doc: req.body.document,
          });
        }
      )
    } else {
      // 元素不存在,追加新元素
      User.findOneAndUpdate(
        { _id: req.profile._id },
        { $push: { documents: targetDoc } },
        { new: true, useFindAndModify: false },
        (pushErr) => {
          if (pushErr) {
            return res.status(400).json({
              error: "Non è stato possibile aggiornare i documenti dell'utente",
            });
          }
          return res.status(200).json({
            status: 'success',
            message: 'Documento caricato con successo',
            doc: req.body.document,
          });
        }
      )
    }
  })
};

注意事项

  • 如果业务中判断元素重复的标识不是_id,只需将代码中所有匹配_id的部分替换为对应字段即可。
  • 高并发场景下优先使用方案一,避免方案二两次查询中间出现数据变更导致的逻辑异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 20:48:03