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
相关产品推荐
相关产品推荐

