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

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();

逻辑说明

  1. 第一次分组先把同一年份、同一股票的所有交易金额求和,得到单股票年度分红总额
  2. 第二次分组按年份汇总,把每个股票的分红数据用$push收集成数组,同时计算年度总金额
  3. 最后用$facet整理成需求要求的外层结构,并用$round保证金额保留两位小数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 08:02:02