如何使用Mongoose实现文档增量合并更新,无需全量拉取已有数据
Mongoose增量合并嵌套数组的高效方案
完全可以通过MongoDB服务端的更新能力实现无需全量拉取文档的增量合并,性能远高于本地拉取合并的方案。
前置修正提示
你当前的代码存在字段名不匹配问题:Schema定义的顶层数组内用户字段为username、动作为actions,但你存储的文档和更新对象里用的是user和action,需要先统一字段名,以下示例统一使用Schema定义的字段名。
推荐方案:基于聚合管道的服务端更新
MongoDB 4.2及以上版本支持用聚合管道执行更新操作,所有逻辑在数据库服务端完成,不需要传输全量文档到业务层,直接就能实现你的合并需求:
// PATCH请求获取的待更新数据(已对齐字段名) const updateData = { users: [ { username: 'John', actions: [{ action: 'Delete', description: 'Delete Post' }] }, { username: 'Robert', actions: [{ action: 'Share', description: 'Share post' }] } ] }; // 替换为你的模型名和目标父文档的查询条件 await UserModel.updateOne( { _id: '目标父文档ID' }, [ { $set: { users: { $reduce: { input: updateData.users, initialValue: "$users", in: { $let: { vars: { currentUser: "$$this", existIdx: { $indexOfArray: ["$$value.username", "$$this.username"] } }, in: { $cond: { // 用户已存在,追加新动作 if: { $gte: ["$$existIdx", 0] }, then: { $concatArrays: [ { $slice: ["$$value", 0, "$$existIdx"] }, [ { username: "$$currentUser.username", actions: { $concatArrays: [ { $arrayElemAt: ["$$value.actions", "$$existIdx"] }, "$$currentUser.actions" ] } } ], { $slice: ["$$value", { $add: ["$$existIdx", 1] }, { $size: "$$value" }] } ] }, // 用户不存在,新增用户条目 else: { $concatArrays: ["$$value", ["$$currentUser"]] } } } } } } } } } ] );
可选优化
- 如果你需要对用户的动作去重,把合并动作的
$concatArrays替换为$setUnion即可 - 如果每次更新的用户数量较多,可以拆分为
bulkWrite批量操作,性能同样优于全量拉取方案 - 嵌套数组层级更深的场景也可以用同样的逻辑扩展,只需要调整
$reduce的遍历层级即可
内容的提问来源于stack exchange,提问作者matan
相关产品推荐
相关产品推荐

