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

MongoDB聚合问题:按股票分组取最近N条文档计算均值

MongoDB 按股票代码分组取最近N条计算均值实现

问题复现

示例数据

[
  {
    ticker : "AAPL",
    date : ISODate("2016-10-18 08:00.000"),
    value : 120
  },
  {
    ticker : "MSFT",
    date : ISODate("2016-10-18 08:00.000"),
    value : 50
  },
  {
    ticker : "AAPL",
    date : ISODate("2016-12-18 08:00.000"),
    value : 160
  },
  {
    ticker : "MSFT",
    date : ISODate("2017-10-18 08:00.000"),
    value : 40
  }
]

需求

  • 按ticker字段分组
  • 每组内按date倒序排列,取最近的N条文档;若组内文档总数不足N条,则取全部现有文档
  • 对筛选出的文档,计算value字段的平均值(即value总和 / 实际参与计算的文档数)
  • 返回格式如下:
[
  {
    _id: "AAPL",
    calculation: 140 // 示例值,对应N=2时AAPL的均值
  }
]

原有方案缺陷

原有聚合管道存在两个明确问题:

  • 全局排序后按N * 股票总数截断的逻辑不可靠:若部分股票的文档总数不足N条,截断后其他股票会拿到超过N条的数据,样本选取完全错误
  • 分组阶段仅对value求和,未统计组内实际参与计算的文档数量做除法,无法得到正确平均值

正确实现方案

方案1:MongoDB 5.0+ 推荐(窗口函数实现,性能最优)

直接使用$setWindowFields窗口函数实现分组内排序排名,不需要提前统计股票总数,自动适配单组数据量不足N的场景,将代码中的N替换为你需要取的最近文档条数即可:

[
  // 按ticker分区,分区内按date倒序,给每条文档生成组内排名
  {
    $setWindowFields: {
      partitionBy: "$ticker",
      sortBy: { date: -1 },
      output: {
        groupRank: { $documentNumber: {} }
      }
    }
  },
  // 过滤掉每组排名超过N的文档,仅保留最近N条
  {
    $match: {
      groupRank: { $lte: N }
    }
  },
  // 按ticker分组,统计value总和、实际文档数
  {
    $group: {
      _id: "$ticker",
      sumValue: { $sum: "$value" },
      count: { $sum: 1 }
    }
  },
  // 计算平均值,输出要求格式
  {
    $project: {
      calculation: { $divide: ["$sumValue", "$count"] }
    }
  }
]

逻辑说明:

  • $setWindowFields会自动按ticker拆分独立分区,每个分区内按日期从新到旧排序,$documentNumber会给每条文档生成从1开始的连续组内排名,完全不受其他分组数据量的影响
  • $match阶段仅保留组内排名≤N的文档,单组文档数不足N时会保留全部数据,不会出现多取、少取的问题
  • 分组阶段同时统计value总和与实际文档数,最后通过$divide计算平均值,自动适配组内文档数不足N的场景

以示例数据为例,N=2时返回结果:

[
  { _id: "AAPL", calculation: 140 },
  { _id: "MSFT", calculation: 45 }
]

N=1时返回结果:

[
  { _id: "AAPL", calculation: 160 },
  { _id: "MSFT", calculation: 40 }
]

方案2:MongoDB 5.0以下版本兼容方案

如果使用的MongoDB版本低于5.0不支持窗口函数,可以用分组后数组切片的方式实现,注意单组数据量过大(单ticker下文档超10万条)时该方案会占用较多内存,优先使用方案1:

[
  // 全局按date倒序排序
  { $sort: { date: -1 } },
  // 按ticker分组,按排序顺序把value存入数组
  {
    $group: {
      _id: "$ticker",
      allValues: { $push: "$value" }
    }
  },
  // 对数组切片取前N个元素,计算平均值
  {
    $project: {
      calculation: {
        $divide: [
          { $sum: { $slice: ["$allValues", N] } },
          { $size: { $slice: ["$allValues", N] } }
        ]
      }
    }
  }
]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 01:30:45