如何在MongoDB中通过$inc自增ID后使用$push插入子文档
问题根因
你的写法存在语法逻辑错误:$inc 是MongoDB的顶层更新操作符,只能放在更新参数的最外层,不能嵌套在 $push 的插入子文档对象中。你当前的写法里MongoDB会把$inc当成子文档的普通字段处理,不会触发任何自增逻辑,所以id字段会一直读取默认值1,移除默认值后就会缺失该字段。
最简单的解决方案
提供两种适配不同场景的原子操作方案,都不需要额外查询,避免并发冲突:
方案1:新增计数器字段(推荐,支持删除子文档场景)
不会出现id重复问题,适用所有业务场景:
- 先调整Schema定义,给主文档新增自增计数器:
const activitySchema = new mongoose.Schema({ userid:{ type:String, required:true }, // 新增计数器,记录下一个子文档要分配的ID,初始值为1 nextActivityId: { type: Number, default: 1 }, activity:[subSchema] })
- 调整更新语句,使用MongoDB 4.2+支持的聚合管道更新(Mongoose直接兼容):
findOneAndUpdate( { userid: userid }, // 用数组包裹表示走聚合更新管道,支持引用文档自身的字段值 [ // 第一步:计数器自增1 { $set: { nextActivityId: { $add: ["$nextActivityId", 1] } } }, // 第二步:插入新子文档,用刚自增后的计数器作为id { $push: { activity: { id: "$nextActivityId", description: "Test", // 聚合更新不会自动触发Schema默认值,手动补充时间字段 time: new Date() } } } ], { new: true } // 可选,配置后返回更新后的最新文档 )
方案2:用数组长度生成ID(无额外字段,仅适用不删除子文档的场景)
如果你的业务逻辑不会删除activity数组里的子文档,可以不用加额外字段,直接用数组长度计算id:
findOneAndUpdate( { userid: userid }, [ { $push: { activity: { // 取当前数组长度+1作为新id id: { $add: [{ $size: "$activity" }, 1] }, description: "Test" } } } ], { new: true } )
注意:如果存在删除子文档的操作,数组长度会变短,该方案会出现id重复的问题。
内容的提问来源于stack exchange,提问作者Tibin Sunny
相关产品推荐
相关产品推荐

