MongoDB中使用$addToSet后如何分组求和(Mongoose实现)
MongoDB聚合查询实现年度及股票分红分组统计
交易数据示例
[{ "type" : "DIVIDEND_OR_INTEREST", "netAmount" : 2.43, "transactionDate" : "2019-01-01T17:02:36+0000", "transactionId" : 1, "transactionItem" : { "instrument" : { "symbol" : "SPHD" } } }, { "type" : "DIVIDEND_OR_INTEREST", "netAmount" : 5.00, "transactionDate" : "2019-01-01T17:02:36+0000", "transactionId" : 2, "transactionItem" : { "instrument" : { "symbol" : "ATT" } } }, { "type" : "DIVIDEND_OR_INTEREST", "netAmount" : 2.43, "transactionDate" : "2019-02-01T17:02:36+0000", "transactionId" : 3, "transactionItem" : { "instrument" : { "symbol" : "SPHD" } } }, { "type" : "DIVIDEND_OR_INTEREST", "netAmount" : 5.00, "transactionDate" : "2019-02-01T17:02:36+0000", "transactionId" : 4, "transactionItem" : { "instrument" : { "symbol" : "ATT" } } }]
需求说明
需要按年份分组统计年度分红总金额,同时按股票代码(symbol)分组统计对应年度分红金额,最终输出格式如下:
{ "year": [ { "year": "2019", "totalYear": 14.86, "dividends": [ { "symbol": "ATT", "amount": 10.00 }, { "symbol": "SPHD", "amount": 4.86 } ] } ] }
现有代码问题
目前用Mongoose编写的聚合查询里,$addToSet只能将单条交易的股票和金额添加到数组,无法自动对同一只股票的金额求和,导致dividends数组里是单条记录而非汇总后的结果。希望完全通过MongoDB查询实现,不依赖应用层处理。现有代码如下:
const [transactions] = await Transaction.aggregate([ { $match: { type: TransactionType.DIVIDEND_OR_INTEREST, netAmount: { $gte: 0 } } }, { $facet: { year: [ { $group: { _id: { $dateToString: { format: '%Y', date: '$transactionDate' } }, totalYear: { $sum: '$netAmount' }, dividends: { $addToSet: { symbol: '$transactionItem.instrument.symbol', amount: '$netAmount', }, }, }, }, { $sort: { _id: 1 } }, { $project: { year: '$_id', totalYear: { $round: ['$totalYear', 2] }, dividends: '$dividends', _id: false, }, }, ], }, }, ]).exec();
解决方案
调整聚合管道,先按年份+股票代码分组求和,再按年份汇总,就能得到正确的分组统计结果:
const [transactions] = await Transaction.aggregate([ // 筛选符合条件的交易记录 { $match: { type: TransactionType.DIVIDEND_OR_INTEREST, netAmount: { $gte: 0 } } }, // 第一步分组:按年份+股票代码,计算单只股票的年度分红总额 { $group: { _id: { year: { $dateToString: { format: '%Y', date: '$transactionDate' } }, symbol: '$transactionItem.instrument.symbol' }, amount: { $sum: '$netAmount' } } }, // 第二步分组:按年份汇总,计算年度总金额并收集各股票的分红数据 { $group: { _id: '$_id.year', totalYear: { $sum: '$amount' }, dividends: { $push: { symbol: '$_id.symbol', amount: { $round: ['$amount', 2] } } } } }, // 按年份升序排序 { $sort: { _id: 1 } }, // 格式化输出字段,处理金额精度 { $project: { year: '$_id', totalYear: { $round: ['$totalYear', 2] }, dividends: 1, _id: false } }, // 包装成需求的外层结构 { $facet: { year: '$$ROOT' } } ]).exec();
逻辑说明
- 第一次分组先把同一年份、同一股票的所有交易金额求和,得到单股票年度分红总额
- 第二次分组按年份汇总,把每个股票的分红数据用
$push收集成数组,同时计算年度总金额 - 最后用
$facet整理成需求要求的外层结构,并用$round保证金额保留两位小数
内容的提问来源于stack exchange,提问作者Yamaha32088
相关产品推荐
相关产品推荐

