如何在Mongoose中为文档添加基于前置文档求和的新字段?
问题描述
需要将集合按日期排序后,为每个文档添加一个新字段sum,其值为当前文档及之前所有文档的value字段之和(第一个文档的sum为自身value,后续文档依次累加前置所有条目),用于生成折线图的累计数据。
输入示例
// 集合中的原始文档 { type: 'typeX', date: '01-01-2000', value: 10 } { type: 'typeY', date: '01-03-2000', value: -5 } { type: 'typeX', date: '01-02-2000', value: 3 } { type: 'typeX', date: '01-03-2000', value: -1 } { type: 'typeY', date: '01-01-2000', value: 10 }
输出示例
{ type: 'typeX', date: '01-01-2000', value: 10, sum: 10} { type: 'typeY', date: '01-01-2000', value: 10, sum: 10} { type: 'typeX', date: '01-02-2000', value: 3, sum: 13} { type: 'typeY', date: '01-03-2000', value: -5, sum: 5} { type: 'typeX', date: '01-03-2000', value: -1, sum: 12}
能否通过Mongoose的aggregate或其他方式实现该需求?
解决方案
完全可以通过Mongoose的聚合管道实现,推荐使用MongoDB 5.0及以上版本支持的窗口函数,语法简洁且性能高效。
实现代码
const result = await YourModel.aggregate([ // 第一步:转换字符串日期为Date类型,按日期升序排序 { $sort: { $dateFromString: { dateString: "$date", format: "%d-%m-%Y" // 匹配输入的DD-MM-YYYY日期格式 }: 1 } }, // 第二步:用窗口函数计算累计求和 { $setWindowFields: { sortBy: { $dateFromString: { dateString: "$date", format: "%d-%m-%Y" }: 1 }, output: { sum: { $sum: "$value", window: { documents: ["unbounded", "current"] // 窗口范围:从第一个文档到当前文档 } } } } } ]);
代码说明
- $sort阶段:输入的
date是字符串格式(DD-MM-YYYY),直接按字符串排序会导致顺序错误,因此用$dateFromString转换为标准Date类型后再排序,确保日期从早到晚的顺序正确。 - $setWindowFields阶段:核心逻辑,通过定义窗口范围(集合起始到当前文档),用
$sum对窗口内的value字段进行累计求和,结果存入新字段sum。
旧版本MongoDB兼容方案(低于5.0)
如果MongoDB版本低于5.0,无法使用窗口函数,可通过以下方式实现:
const result = await YourModel.aggregate([ // 添加日期对象字段用于排序 { $addFields: { dateObj: { $dateFromString: { dateString: "$date", format: "%d-%m-%Y" } } } }, { $sort: { dateObj: 1 } }, // 将所有文档合并为数组 { $group: { _id: null, docs: { $push: "$$ROOT" } } }, // 遍历数组计算累计和 { $project: { docs: { $map: { input: "$docs", as: "doc", in: { $mergeObjects: [ "$$doc", { sum: { $sum: { $slice: ["$docs.value", 0, { $add: [{ $indexOfArray: ["$docs._id", "$$doc._id"] }, 1] }] } } } ] } } } } }, // 展开数组回到单个文档 { $unwind: "$docs" }, // 移除临时字段,还原原结构 { $replaceRoot: { newRoot: { $omit: ["$$ROOT.docs.dateObj"] } } } ]);
该方案性能低于窗口函数,数据量大时会有明显延迟,建议优先升级MongoDB版本使用窗口函数方案。
内容的提问来源于stack exchange,提问作者Firatov
相关产品推荐
相关产品推荐

