MongoDB 5.0 起必须用 $setWindowFields 实现滑动平均,需前置 $sort、显式 partitionBy: null、sortBy 单字段排序及 documents/range 窗口定义,缺一不可。

滑动平均值在 MongoDB 5.0 中必须用 $setWindowFields,没有替代方案
MongoDB 5.0 是首个支持窗口函数的版本,$setWindowFields 是唯一能做滑动(滚动)平均的聚合阶段。此前版本只能靠应用层计算或预聚合,无法在单次查询中动态按时间/顺序窗口求均值。
注意:$setWindowFields 要求集合有明确的排序字段(如 timestamp 或 seq),且该字段必须建索引,否则会报错 "Pipeline requires a sort before setWindowFields"。
$setWindowFields 的三个必要组成部分缺一不可
一个可用的滑动平均必须同时指定:partitionBy(可选但建议显式设为 null)、sortBy(必需)、output(含 $avg + window)。
-
sortBy: { timestamp: 1 }—— 必须是单字段升序或降序,不支持复合排序 -
window: { documents: [-2, 0] }—— 表示“当前文档及其前两行”,documents窗口最常用;也可用range(需字段为数字/日期且已索引) -
partitionBy: null—— 显式设为null表示不分组全局滑动;若省略,MongoDB 可能意外按 _id 分区,导致结果错乱
示例:对每条记录计算最近 3 条(含自身)的 value 平均值
[
{ $sort: { timestamp: 1 } },
{
$setWindowFields: {
partitionBy: null,
sortBy: { timestamp: 1 },
output: {
moving_avg: {
$avg: "$value",
window: { documents: [-2, 0] }
}
}
}
}
]
常见错误:窗口越界、排序缺失、字段类型不匹配
实际使用中最容易卡在这三类问题上:
- 没加
$sort阶段就直接写$setWindowFields—— 报错"setWindowFields requires a prior $sort",且这个$sort必须紧邻其前(中间不能插其他阶段) -
documents: [-n, 0]中n超出数据边界(比如开头两条记录尝试取前 5 行)—— MongoDB 自动忽略越界部分,不报错但结果偏小,需用$cond补零或标记 -
$avg字段是字符串或null—— 会被静默跳过,导致平均值偏低;建议先用$toDouble或$ifNull清洗
补零示例(当窗口内有效数值不足 3 个时设为 null):
{
$setWindowFields: {
output: {
moving_avg: {
$avg: {
$cond: [
{ $gte: [{ $sum: "$value" }, 0] }, // 粗略判断非空(实际应更严谨)
"$value",
"$$REMOVE"
]
},
window: { documents: [-2, 0] }
}
}
}
}
性能敏感点:索引、文档窗口长度、内存限制
$setWindowFields 不走索引加速窗口计算,只依赖 $sort 阶段的索引。窗口越大,内存占用越高:
-
documents: [-99, 0](100 行窗口)在万级数据集上基本无压力;超过 500 行可能触发"Sort exceeded memory limit" -
range: { bounds: [ -86400000, 0 ] }(24 小时窗口)要求sortBy字段是Date类型,且对应索引存在,否则退化为全表扫描 - 聚合管道不能分片执行
$setWindowFields—— 如果集合已分片,该阶段会在 mongos 合并后执行,大数据量下延迟明显
真实场景中,如果窗口固定且高频访问,建议用物化视图($merge 到新集合)代替实时计算。











