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

Mongoose:如何利用当前数据更新嵌套数组中的元素?

MongoDB数组元素更新时$concatArrays不生效的解决方法

集合初始数据

const documents = [
    {
        _id: '1',
        items: [
            {
                type: 'plastic',
                color: 'blue',
                date: '2021-02-01',
                oldValues: [{ color: 'light-blue', date: '2021-01-01' }],
            },
            {
                type: 'plastic',
                color: 'black',
                date: '2021-03-01',
                oldValues: [
                    { color: 'white', date: '2021-01-01' },
                    { color: 'grey', date: '2021-02-01' },
                ],
            },
        ],
    },
    {
        _id: '2',
        items: [
            {
                type: 'plastic',
                color: 'violet',
                date: '2021-02-01',
                oldValues: [{ color: 'pink', date: '2021-01-01' }],
            },
            {
                type: 'wood',
                color: 'black',
                date: '2021-03-01',
                oldValues: [
                    { color: 'white', date: '2021-01-01' },
                    { color: 'orange', date: '2021-02-01' },
                ],
            },
        ],
    },
];

需求说明

找到集合中文档的items数组里,满足type: 'plastic'且color: 'black'的元素:

  1. 将该元素的color字段更新为green
  2. 将该元素更新前的color和date数据追加到其oldValues数组中

错误的更新语句

以下语句执行后,目标元素的color成功更新,但oldValues被直接赋值为聚合表达式对象,未计算出预期的数组:

db.collection.update(
    { items: { $elemMatch: { color: 'black', type: 'plastic' } } },
    {
        $set: {
            'items.$[item].color': 'green',
            'items.$[item].date': '2021-04-01',
            'items.$[item].oldValues': {
                $concatArrays: [
                    { $ifNull: ['$items.$[item].oldValues', []] },
                    [{ color: '$items.$[item].color', date: '$items.$[item].date' }],
                ],
            },
        },
    },
    { arrayFilters: [{ 'item.color': 'black', 'item.type': 'plastic' }] },
);

问题原因

普通$set更新操作不支持直接使用聚合运算符(如$concatArrays、$ifNull),只能接受常量或字段引用。要使用聚合表达式处理更新逻辑,需采用MongoDB 4.2及以上版本支持的管道式更新(即更新操作符参数为数组形式的聚合管道)。

解决方案

方法1:管道式更新(推荐)

利用聚合管道遍历数组,匹配目标元素并执行更新逻辑:

db.collection.update(
  { "items": { $elemMatch: { color: "black", type: "plastic" } } },
  [
    {
      $set: {
        items: {
          $map: {
            input: "$items",
            as: "item",
            in: {
              $cond: {
                if: { $and: [{ $eq: ["$$item.type", "plastic"] }, { $eq: ["$$item.color", "black"] }] },
                then: {
                  type: "$$item.type",
                  color: "green",
                  date: "$$item.date", // 如需修改date可替换为'2021-04-01'
                  oldValues: {
                    $concatArrays: [
                      { $ifNull: ["$$item.oldValues", []] },
                      [{ color: "$$item.color", date: "$$item.date" }]
                    ]
                  }
                },
                else: "$$item"
              }
            }
          }
        }
      }
    }
  ],
  { multi: true }
)
  • $map遍历items数组,对每个元素进行判断
  • $cond筛选出符合type: plastic且color: black的元素,构造新对象实现color更新和oldValues追加
  • multi: true确保所有匹配的文档都被更新

方法2:事务实现(原子性保障)

如果需要保证多文档更新的原子性,可通过MongoDB事务实现:

// 开启会话与事务
const session = db.getMongo().startSession();
session.startTransaction();

try {
  // 指定目标库和集合
  const targetColl = session.getDatabase("your_db_name").collection("your_collection_name");

  // 查询所有符合条件的文档
  const matchedDocs = targetColl.find({ 
    "items": { $elemMatch: { color: "black", type: "plastic" } } 
  }).toArray();

  // 遍历文档执行更新
  matchedDocs.forEach(doc => {
    const updatedItems = doc.items.map(item => {
      if (item.type === "plastic" && item.color === "black") {
        return {
          ...item,
          color: "green",
          oldValues: [
            ...(item.oldValues || []),
            { color: item.color, date: item.date }
          ]
        };
      }
      return item;
    });

    // 更新当前文档
    targetColl.updateOne(
      { _id: doc._id },
      { $set: { items: updatedItems } }
    );
  });

  // 提交事务
  session.commitTransaction();
  print("事务执行成功");
} catch (error) {
  print(`事务执行失败,回滚: ${error}`);
  session.abortTransaction();
} finally {
  // 关闭会话
  session.endSession();
}
  • 通过会话开启事务,先查询所有匹配文档
  • 在客户端处理数组元素的更新逻辑,再逐个更新文档
  • 若过程中出现异常则回滚事务,确保操作原子性

验证结果

执行上述任一方法后,目标元素的color会变为green,oldValues数组会追加更新前的{ color: 'black', date: '2021-03-01' },完全符合预期。

内容的提问来源于stack exchange,提问作者Majid Rafei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 04:40:55