$fill 必须嵌套在 $setWindowFields 中,因其是其子操作符,依赖 sortBy 定义的排序和 partitionByFields 定义的分组才能确定前后非空值;缺 sortBy 报错,缺 partitionByFields 会导致跨设备污染。

$fill 阶段在 MongoDB 5.0+ 中能直接填充缺失值,但仅限于前向/后向/线性插值三种模式,且必须配合 $setWindowFields 使用;单独用 $fill 不生效。
为什么 $fill 必须嵌套在 $setWindowFields 里
MongoDB 的 $fill 不是独立聚合阶段,而是 $setWindowFields 的一个子操作符。它依赖窗口定义的排序和分组逻辑才能确定“前后”非空值——没有 sortBy,就无法判断哪个值该用来插值。
- 漏写
sortBy:聚合报错"sortBy is required for $fill" - 误把
$fill当作顶层阶段写在$project后面:整个阶段被忽略,输出无变化 -
partitionByFields为空但数据实际有多个设备/传感器:所有时间点被混在一起插值,导致跨设备污染
$fill 的三种 method 怎么选
method 决定插值逻辑,不是所有场景都适合线性:
-
method: "prev":用上一个非空值覆盖后续连续 null,适合状态类字段(如设备开关状态) -
method: "next":用下一个非空值反向填充,适合补录延迟上报的数据 -
method: "linear":要求前后都有非空值,且数值可线性映射(如温度、股价),否则中间段仍为 null
注意:method: "linear" 对时间字段本身不敏感——它只看排序后相邻文档的字段值差,不自动按时间跨度加权。如果时间间隔不均(如 1h、3h、30min 混杂),结果会失真。
常见错误:null 没填上,但也没报错
这通常是因为数据不满足 $fill 的隐含前提:
- 窗口内第一个文档字段就是 null,且
method是"prev":无“前值”,保持 null - 窗口内最后一个文档字段是 null,且
method是"next":无“后值”,保持 null - 使用
method: "linear"时,null 前后紧邻的两个值都是 null:线性插值需要至少一个前值 + 一个后值,缺一不可 -
sortBy字段存在重复值(如多条记录时间戳完全相同):$setWindowFields直接失败,不会进入$fill
对比 $linearFill 和 $fill 的实际差异
二者都能做线性插值,但调用位置和灵活性不同:
-
$fill只能在$setWindowFields的output中作为字段赋值表达式,例如:{ price: { $fill: { value: "$price", method: "linear" } } } -
$linearFill是独立运算符,可直接用于$setWindowFields的output,且支持指定源字段与目标字段分离,例如:{ filledPrice: { $linearFill: "$price" } }—— 这样原始price字段保留不变 - 性能上无显著差别,但
$linearFill更适合需保留原始字段 + 新增填充字段的场景
真正容易被忽略的是:无论用哪种,只要窗口未显式定义 partitionByFields,整个集合会被当作一个大窗口处理——高频时间序列(如每秒采样)一旦跨天或跨设备未分区,排序和插值会变慢且结果不可控。


















