Node.js中MongoDB嵌套对象数组条件更新实现
MongoDB嵌套数组更新:存在日期则增量计数,不存在则新增条目
问题背景
使用Node.js操作MongoDB,需要对存储带日期戳的嵌套对象数组执行更新,现有集合文档结构如下:
{ _id: 629f2f5e7aa147d6503957d0, kid_id: "629f2f5e7aa147d6503957ce", inventory: [ { size: "0", purchased: "0", used: [{}], _id: "111111" }, { size: "1", purchased: "10", used: [ { date: "06/08/22", count: "2"} ], _id: "222222" } ] }
需求说明
- 基础需求:定位到
inventory数组中size值为1的条目,向其内部的used数组追加{ date: "06/09/22", count: "4"}对象 - 长期逻辑:
- 若
used数组中已存在相同date值的条目,直接对该条目的count值做增量更新 - 若传入的
date值在used数组中不存在,则新增对应的日期统计对象
- 若
原有问题代码
最初编写的代码存在两处核心错误:一是没有处理used二级数组的匹配逻辑,二是没有兼容「存在则更新、不存在则新增」的数组操作逻辑,无法满足需求:
router.put('/used', auth, async (req, res) => { let kidID = req.body.kid_id; let size = req.body.size; try { const record = await InventoryRecord.updateOne( { kid_id: kidID }, { $inc: { 'inventory.$[el].used': -1 } }, { arrayFilters: [{ 'el.size': size }] } ); console.log(record); res.send(record); } catch (error) { res.send({ message: error.message }); } });
可行实现方案
MongoDB 4.2及以上版本支持聚合管道更新,可以通过$cond结合数组操作单次请求完成需求,无需多次查询,实现代码如下:
router.put('/used', auth, async (req, res) => { const { kid_id: kidID, size, updateDate, addCount } = req.body; // 入参示例:kidID为对应文档关联id,size="1",updateDate="06/09/22",addCount=4 try { const record = await InventoryRecord.updateOne( { kid_id: kidID }, [ { $set: { inventory: { $map: { input: "$inventory", in: { $cond: [ { $eq: ["$$this.size", size] }, // 匹配到对应size的inventory条目 { $mergeObjects: [ "$$this", { used: { $cond: [ // 校验used数组中是否已存在对应日期条目 { $in: [updateDate, "$$this.used.date"] }, // 日期存在:遍历used数组给对应条目的count做增量 { $map: { input: "$$this.used", in: { $cond: [ { $eq: ["$$this.date", updateDate] }, { $mergeObjects: [ "$$this", // 示例中count存为字符串,此处做类型转换,若存储为Number类型可去掉$toInt { count: { $add: [{ $toInt: "$$this.count" }, addCount] } } ] }, "$$this" ] } } }, // 日期不存在:拼接新的日期统计对象到used数组末尾 { $concatArrays: ["$$this.used", [{ date: updateDate, count: String(addCount) }]] } ] } } ] }, // 不匹配size的inventory条目原样保留 "$$this" ] } } } } } ] ); console.log(record); res.send(record); } catch (error) { res.send({ message: error.message }); } });
优化建议
- 建议将文档中
purchased、count这类数值字段从字符串类型改为Number类型,避免每次更新做类型转换,减少性能损耗和类型异常 - 如果使用的MongoDB版本低于4.2不支持聚合更新,可以分两步实现:先查询对应文档判断目标日期是否存在,再根据结果执行
$inc增量或$push追加操作,逻辑更直观但会多一次数据库查询
内容的提问来源于stack exchange,提问作者laiello
相关产品推荐
相关产品推荐

