如何对Mongoose文档players数组points求和并更新到totalTeamPoints字段
实现方案
共有两种常用的可行方案,你可以根据业务场景选择:
方案1:使用Mongoose中间件自动计算(推荐普通场景使用)
通过给Schema绑定前置钩子,只要触发文档保存/更新操作就自动计算总积分,不需要业务代码额外处理:
第一步:定义Schema
const mongoose = require('mongoose'); const { Schema } = mongoose; // 子文档:球员信息 const playerSchema = new Schema({ _id: String, points: Number }); // 主文档:团队信息 const teamSchema = new Schema({ _id: { teamName: String, tournamentId: String }, players: [playerSchema], totalTeamPoints: { type: Number, default: 0 }, __v: Number });
第二步:绑定save操作钩子
覆盖先查文档、修改后调用save()的更新场景:
teamSchema.pre('save', function(next) { // 累加所有球员的points字段 this.totalTeamPoints = this.players.reduce((sum, player) => sum + player.points, 0); next(); });
第三步:绑定更新操作钩子
覆盖直接调用updateOne/findOneAndUpdate/updateMany等更新方法的场景:
teamSchema.pre(/^update|^findOneAndUpdate/, async function(next) { // 查询待更新的原始文档 const targetDoc = await this.model.findOne(this.getQuery()); if (!targetDoc) return next(); // 计算总积分并写入更新参数 const totalPoints = targetDoc.players.reduce((sum, player) => sum + player.points, 0); this.set({ totalTeamPoints: totalPoints }); next(); });
注意:该方案会额外产生一次查询请求获取原始文档,高并发或批量更新场景下性能会受影响
方案2:使用MongoDB聚合更新管道(推荐高性能/批量更新场景使用)
直接在数据库层面单次请求完成「修改球员积分」+「计算团队总积分」两个操作,不需要依赖中间件,性能更高:
// 示例:给指定队伍中_id为113069的球员增加10积分,同时更新团队总积分 await TeamModel.updateOne( // 查询条件:定位到目标队伍和目标球员 { "_id.teamName": "BILALATI6449", "_id.tournamentId": "197831", "players._id": "113069" }, // 聚合更新管道:按顺序执行两个操作 [ // 1. 更新目标球员的积分 { $set: { "players.$[targetPlayer].points": { $add: ["$$targetPlayer.points", 10] } } }, // 2. 重新计算所有球员的积分总和,写入totalTeamPoints字段 { $set: { totalTeamPoints: { $sum: "$players.points" } } } ], // 数组过滤条件:定位到要修改的球员 { arrayFilters: [{ "targetPlayer._id": "113069" }] } );
内容的提问来源于stack exchange,提问作者Muhammad Danyal
相关产品推荐
相关产品推荐

