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
相关产品推荐
相关产品推荐

