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

如何在MongoDB/Mongoose中优雅实现数组对象的更新或新增?

在MongoDB/Mongoose中实现通知更新逻辑

集合结构

以下是简化后的User集合结构:

[
  {
    _id: "10",
    groups: ["1"],
    notifications: [
      {
        notificationType: "message-like",
        msgId: "3",
        read: false,
        likers: ["steve", "joe"]
      }
    ]
  },
  {
    _id: "20",
    groups: ["1", "2"],
    notifications: [
      {
        notificationType: "message-like",
        msgId: "1",
        read: false,
        likers: ["frank", "bob"]
      },
      {
        notificationType: "message-like",
        msgId: "2",
        read: false,
        likers: ["bob"]
      }
    ]
  }
]

需求

  • 查找_id匹配的文档
  • 在notifications数组中查找notificationType和msgId同时匹配的对象
  • 若找到匹配对象:
    • 向likers数组添加新元素
    • 将该对象移至notifications数组首位
    • 若该对象的read为true,则将其设为false
  • 若未找到匹配对象:向notifications数组新增一个通知对象

参考JavaScript逻辑

for (let doc of documents) {
  if (doc._id === "20") {
      let notificationFound = false
      doc.notifications.forEach((notif, i) => {
          if (
              notif.notificationType === "message-like"
              && notif.msgId === "2"
          ) {
              notif.likers.push("joe")
              if (notif.read === true) notif.read = false
              doc.notifications.unshift(doc.notifications.splice(i, 1)[0]) // 移至数组首位
              notificationFound = true
          }
      })
      if (!notificationFound) doc.notifications.push(newNotificationObject)
  }
}

MongoDB/Mongoose实现方案

可以通过MongoDB原子更新操作结合聚合表达式实现,避免先查询再修改的非原子操作,保证数据一致性。

1. MongoDB原生语法(findOneAndUpdate)

db.users.findOneAndUpdate(
  { _id: "20" },
  [
    {
      $set: {
        notifications: {
          $cond: {
            // 判断是否存在匹配的通知
            if: { $gt: [{ $size: { $filter: { input: "$notifications", cond: { $and: [{ $eq: ["$$this.notificationType", "message-like"] }, { $eq: ["$$this.msgId", "2"] }] } } } }, 0] },
            then: {
              $let: {
                vars: {
                  // 提取匹配的通知和非匹配的通知
                  matched: { $first: { $filter: { input: "$notifications", cond: { $and: [{ $eq: ["$$this.notificationType", "message-like"] }, { $eq: ["$$this.msgId", "2"] }] } } },
                  rest: { $filter: { input: "$notifications", cond: { $not: { $and: [{ $eq: ["$$this.notificationType", "message-like"] }, { $eq: ["$$this.msgId", "2"] }] } } }
                },
                in: [
                  // 处理匹配通知:添加likers、重置read为false,放在数组首位
                  {
                    $mergeObjects: [
                      "$$matched",
                      { likers: { $concatArrays: ["$$matched.likers", ["joe"]] } },
                      { read: false }
                    ]
                  },
                  // 拼接剩余通知
                  "$$rest"
                ]
              }
            },
            // 未找到则新增通知
            else: {
              $concatArrays: [
                "$notifications",
                [
                  {
                    notificationType: "message-like",
                    msgId: "2",
                    read: false,
                    likers: ["joe"]
                  }
                ]
              ]
            }
          }
        }
      }
    }
  ],
  { returnNewDocument: true } // 返回更新后的文档
)

2. Mongoose写法

基于定义好的User模型调用findOneAndUpdate,语法与原生一致:

const updatedUser = await User.findOneAndUpdate(
  { _id: "20" },
  [
    {
      $set: {
        notifications: {
          $cond: {
            if: { $gt: [{ $size: { $filter: { input: "$notifications", cond: { $and: [{ $eq: ["$$this.notificationType", "message-like"] }, { $eq: ["$$this.msgId", "2"] }] } } } }, 0] },
            then: {
              $let: {
                vars: {
                  matched: { $first: { $filter: { input: "$notifications", cond: { $and: [{ $eq: ["$$this.notificationType", "message-like"] }, { $eq: ["$$this.msgId", "2"] }] } } },
                  rest: { $filter: { input: "$notifications", cond: { $not: { $and: [{ $eq: ["$$this.notificationType", "message-like"] }, { $eq: ["$$this.msgId", "2"] }] } } }
                },
                in: [
                  {
                    $mergeObjects: [
                      "$$matched",
                      { likers: { $concatArrays: ["$$matched.likers", ["joe"]] } },
                      { read: false }
                    ]
                  },
                  "$$rest"
                ]
              }
            },
            else: {
              $concatArrays: [
                "$notifications",
                [
                  {
                    notificationType: "message-like",
                    msgId: "2",
                    read: false,
                    likers: ["joe"]
                  }
                ]
              ]
            }
          }
        }
      }
    }
  ],
  { new: true } // 返回更新后的文档
);

逻辑说明

  • 用$filter筛选出匹配/非匹配的通知项
  • 通过$cond分支处理“存在匹配”和“无匹配”两种场景
  • 匹配时:用$mergeObjects更新通知字段,将处理后的通知放在数组首位再拼接剩余项
  • 未匹配时:用$concatArrays在数组末尾添加新通知
  • 整个操作是原子性的,避免并发场景下的数据冲突

内容的提问来源于stack exchange,提问作者Joey Kyber

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 12:12:46