Mongoose如何实现嵌套数组的条件新增与upsert更新
Mongoose嵌套数组条件更新实现方案
原有代码问题
你之前的写法错误在于无法在$push的元素定义中嵌套另一个$push操作符,MongoDB不支持这种嵌套更新语法,无法直接同时判断数组元素是否存在、执行对应追加/新增逻辑。
你定义的课程Schema如下:
const courseSchema = new mongoose.Schema({ name: String, code: String, class: String, credit: Number, overview: String, prerequisite: String, syllabus: [ { moduleName: String, moduleDetails: String, }, ], materials: [ { moduleNo: Number, moduleMaterials: [String], }, ], teacher: String, students: [String], });
需求逻辑:
- 通过
courseID匹配目标课程记录 - 检查
materials数组中是否存在传入的moduleNo:- 存在:将
materialURL追加到对应项的moduleMaterials数组,保留原有内容 - 不存在:在
materials数组新增对象,包含传入的moduleNo,以及初始化存入当前materialURL的moduleMaterials数组
- 存在:将
推荐实现(MongoDB 4.2+ 原子性写法)
使用聚合管道更新可以单条语句完成所有逻辑,不需要二次查询,原子性更好,代码如下:
Course.updateOne( { _id: courseID }, [ { $set: { materials: { $cond: [ // 校验materials数组中是否已存在当前moduleNo { $in: [moduleNo, "$materials.moduleNo"] }, // 已存在:遍历数组匹配对应项,追加URL到moduleMaterials { $map: { input: "$materials", in: { $cond: [ { $eq: ["$$this.moduleNo", moduleNo] }, { $mergeObjects: [ "$$this", { moduleMaterials: { $concatArrays: ["$$this.moduleMaterials", [materialURL]] } } ] }, "$$this" ] } } }, // 不存在:拼接新增的materials项到数组末尾 { $concatArrays: ["$materials", [{ moduleNo: moduleNo, moduleMaterials: [materialURL] }]] } ] } } } ], { upsert: true }, (err, result) => { if (err) { console.error(err); return; } console.log('更新成功', result); } )
低版本MongoDB兼容写法
如果使用的MongoDB版本低于4.2,不支持聚合管道更新,可以用位置操作符$[<identifier>]搭配兜底查询实现:
Course.updateOne( { _id: courseID }, { // 匹配到已存在的moduleNo时,直接追加URL到对应moduleMaterials数组 $push: { "materials.$[target].moduleMaterials": materialURL } }, { arrayFilters: [ { "target.moduleNo": moduleNo } ] }, (err, result) => { if (err) { console.error(err); return; } // 没有匹配到已存在的moduleNo,说明需要新增materials项 if (result.modifiedCount === 0) { Course.updateOne( { _id: courseID, "materials.moduleNo": { $ne: moduleNo } }, { $push: { materials: { moduleNo: moduleNo, moduleMaterials: [materialURL] } } }, (pushErr, pushRes) => { if (pushErr) console.error(pushErr); else console.log('新增模块资料成功', pushRes); } ) return; } console.log('追加资料成功', result); } )
注意:如果需要在课程本身不存在时自动创建课程文档,可以在更新操作中加上
upsert: true配置。
内容的提问来源于stack exchange,提问作者Jonan Mathew George
相关产品推荐
相关产品推荐

