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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 17:36:28