$fill(聚合)
在此页面上
定义
$fill
5.3 版本中的新增功能。
填充文档中的
null
和缺失的字段值。您可以使用
$fill
来填充缺失的数据点:根据周围的数值排序。
具备固定值。
语法
$fill
阶段采用以下语法:
{ $fill: { partitionBy: <expression>, partitionByFields: [ <field 1>, <field 2>, ... , <field n> ], sortBy: { <sort field 1>: <sort order>, <sort field 2>: <sort order>, ..., <sort field n>: <sort order> }, output: { <field 1>: { value: <expression> }, <field 2>: { method: <string> }, ... } } }
$fill
阶段采用包含以下字段的文档:
字段 | 必要性 | 说明 |
---|---|---|
Optional | 指定一个表达式对文档进行群组。 在 如果省略 partitionBy 和 partitionByFields, partitionBy 和 partitionByFields 互斥。 请参阅示例。 | |
Optional | 指定一个字段数组作为复合键,以便对文档进行分组。在 如果省略 partitionBy 和 partitionByFields, partitionBy 和 partitionByFields 互斥。 | |
如果在至少一个输出 <field> 中指定了方法,则为必填项。 否则为可选。 | 指定每个分区内用于对文档进行排序的字段。使用与 | |
必需 | 指定对象,其中包含要填充缺失值的每个字段。您可以在输出对象中指定多个字段。 对象名称是要填写的字段名称。对象值指定了字段的填充方式。 | |
必需 |
行为和限制
partitionByFields
限制
如果 partitionByFields 数组中有任何字段名称,则 $fill
会返回错误:
求值为非字符串值。
以
$
开头。
linear
行为
linear
填充方法根据序列中周围的非 null
值,使用线性插值填充 null
和缺失字段。
对于字段为
null
或缺失的每个文档,linearFill
会根据 sortBy 顺序,按照周围非null
值之间的缺失值范围比例填充这些字段。为了确定缺失字段的值,linearFill
使用:周围非
null
值之差。周围值之间要填充的
null
字段的数量。
如果根据 sortBy 排序,
linear
方法可以填充多个连续的null
值,前提是这些值的前后都是非null
值。例子
如果集合中包含这些文档:
{ index: 0, value: 0 }, { index: 1, value: null }, { index: 2, value: null }, { index: 3, value: null }, { index: 4, value: 10 } 使用
linear
填充方法填充null
值后,文档变为:{ index: 0, value: 0 }, { index: 1, value: 2.5 }, { index: 2, value: 5 }, { index: 3, value: 7.5 }, { index: 4, value: 10 } null
前后没有非null
值的值仍为null
。如需使用
linear
填充方法,还必须使用 sortBy 字段对数据排序。
有关使用 linear
填充方法的完整示例,请参阅使用线性插值填充缺失字段值。
locf
行为
locf
代表最后的观察结果。
如果填写的字段包含
null
和非空值,locf
会根据 sortBy 排序将null
和缺失值设置为该字段的最后一个已知非空值。如果该字段在分区中仅包含
null
或缺失值,locf
会将该分区的字段值设置为null
。null
而排序顺序中出现在非空值之前的缺失字段值仍为null
。
如需使用
locf
填充方法,还必须使用 sortBy 字段对数据排序。
有关使用 locf
填充方法的完整示例,请参阅根据上一次观察到的值填充缺失字段值。
$fill
和聚合操作符的比较
要填充文档中的 null
和缺失的字段值,您可以使用:
$fill
阶段。使用
$fill
阶段时,输出中指定的字段与源数据使用的字段相同。$linearFill
和$locf
聚合操作符。当您
$linearFill
或$locf
时,可以为不同于作为源数据使用的字段设置值。
示例
本节的示例展示了如何使用 $fill
填充缺失值:
用常量值填充缺失字段值
一家鞋店建立了 dailySales
集合,其中包含总结每天销售情况的文档。这家鞋店销售以下类型的鞋:
boots
sandals
sneakers
创建以下 dailySales
集合:
db.dailySales.insertMany( [ { "date": ISODate("2022-02-02"), "bootsSold": 10, "sandalsSold": 20, "sneakersSold": 12 }, { "date": ISODate("2022-02-03"), "bootsSold": 7, "sneakersSold": 18 }, { "date": ISODate("2022-02-04"), "sneakersSold": 5 } ] )
并非 dailySales
集合中的所有文档都包含每种鞋子类型。如果某一类型的鞋子缺失,则表示在相应日期没有售出该类型的鞋子。
以下示例使用 $fill
将每天销售中缺少的鞋款的销售数量设置为 0
:
db.dailySales.aggregate( [ { $fill: { output: { "bootsSold": { value: 0 }, "sandalsSold": { value: 0 }, "sneakersSold": { value: 0 } } } } ] )
在前面的管道中:
示例输出:
[ { _id: ObjectId("6202df9f394d47411658b51e"), date: ISODate("2022-02-02T00:00:00.000Z"), bootsSold: 10, sandalsSold: 20, sneakersSold: 12 }, { _id: ObjectId("6202df9f394d47411658b51f"), date: ISODate("2022-02-03T00:00:00.000Z"), bootsSold: 7, sneakersSold: 18, sandalsSold: 0 }, { _id: ObjectId("6202df9f394d47411658b520"), date: ISODate("2022-02-04T00:00:00.000Z"), sneakersSold: 5, bootsSold: 0, sandalsSold: 0 } ]
用线性插值填充缺失字段值
创建 stock
集合,其中包含每小时跟踪单个公司的股票价格:
db.stock.insertMany( [ { time: ISODate("2021-03-08T09:00:00.000Z"), price: 500 }, { time: ISODate("2021-03-08T10:00:00.000Z"), }, { time: ISODate("2021-03-08T11:00:00.000Z"), price: 515 }, { time: ISODate("2021-03-08T12:00:00.000Z") }, { time: ISODate("2021-03-08T13:00:00.000Z") }, { time: ISODate("2021-03-08T14:00:00.000Z"), price: 485 } ] )
集合中的某些文档缺少 price
字段。
要使用线性插值填充缺失的 price
值,请将 $fill
与 linear
填充方法一起使用:
db.stock.aggregate( [ { $fill: { sortBy: { time: 1 }, output: { "price": { method: "linear" } } } } ] )
在前面的管道中:
$fill
填充缺失字段的值。sortBy: { time: 1 }
按time
字段以升序排列文档,从最早到最晚。输出指定:
price
作为要填写缺失值的字段。{ method: "linear" }
作为填充方法。linear
填充方法使用price
线性插值 填充缺失的 值price
基于序列中周围的 值。
示例输出:
[ { _id: ObjectId("620ad41c394d47411658b5e9"), time: ISODate("2021-03-08T09:00:00.000Z"), price: 500 }, { _id: ObjectId("620ad41c394d47411658b5ea"), time: ISODate("2021-03-08T10:00:00.000Z"), price: 507.5 }, { _id: ObjectId("620ad41c394d47411658b5eb"), time: ISODate("2021-03-08T11:00:00.000Z"), price: 515 }, { _id: ObjectId("620ad41c394d47411658b5ec"), time: ISODate("2021-03-08T12:00:00.000Z"), price: 505 }, { _id: ObjectId("620ad41c394d47411658b5ed"), time: ISODate("2021-03-08T13:00:00.000Z"), price: 495 }, { _id: ObjectId("620ad41c394d47411658b5ee"), time: ISODate("2021-03-08T14:00:00.000Z"), price: 485 } ]
根据上一次观察到的值填充缺失字段值
创建 restaurantReviews
集合,其中包含一家餐厅在一段时间内的评论得分:
db.restaurantReviews.insertMany( [ { date: ISODate("2021-03-08"), score: 90 }, { date: ISODate("2021-03-09"), score: 92 }, { date: ISODate("2021-03-10") }, { date: ISODate("2021-03-11") }, { date: ISODate("2021-03-12"), score: 85 }, { date: ISODate("2021-03-13") } ] )
集合中的某些文档缺少 score
字段。
如需填充缺失的 score
字段并确保数据中没有空白,请使用 $fill
。在以下示例中,$fill
使用 locf
填充方法,用序列中的前一个 score
值来填充缺失的 score
值:
db.restaurantReviews.aggregate( [ { $fill: { sortBy: { date: 1 }, output: { "score": { method: "locf" } } } } ] )
在前面的管道中:
$fill
填充缺失的score
值。sortBy: { date: 1 }
按date
字段以升序排列文档,从最早到最晚。输出指定:
score
作为要填写缺失值的字段。{ method: "locf" }
作为填充方法。locf
填充方法是用序列中上一次观察到的score
值来填充缺失的score
值。
示例输出:
[ { _id: ObjectId("62040bc9394d47411658b553"), date: ISODate("2021-03-08T00:00:00.000Z"), score: 90 }, { _id: ObjectId("62040bc9394d47411658b554"), date: ISODate("2021-03-09T00:00:00.000Z"), score: 92 }, { _id: ObjectId("62040bc9394d47411658b555"), date: ISODate("2021-03-10T00:00:00.000Z"), score: 92 }, { _id: ObjectId("62040bc9394d47411658b556"), date: ISODate("2021-03-11T00:00:00.000Z"), score: 92 }, { _id: ObjectId("62040bc9394d47411658b557"), date: ISODate("2021-03-12T00:00:00.000Z"), score: 85 }, { _id: ObjectId("62040bc9394d47411658b558"), date: ISODate("2021-03-13T00:00:00.000Z"), score: 85 } ]
为不同分区填充数据
继续以前面的餐厅评论为例,但该集合现在追踪的不是一家餐厅,而是多家餐厅的评论。
创建名为 restaurantReviewsMultiple
的集合,然后用这些文档填充该集合:
db.restaurantReviewsMultiple.insertMany( [ { date: ISODate("2021-03-08"), restaurant: "Joe's Pizza", score: 90 }, { date: ISODate("2021-03-08"), restaurant: "Sally's Deli", score: 75 }, { date: ISODate("2021-03-09"), restaurant: "Joe's Pizza", score: 92 }, { date: ISODate("2021-03-09"), restaurant: "Sally's Deli" }, { date: ISODate("2021-03-10"), restaurant: "Joe's Pizza" }, { date: ISODate("2021-03-10"), restaurant: "Sally's Deli", score: 68 }, { date: ISODate("2021-03-11"), restaurant: "Joe's Pizza", score: 93 }, { date: ISODate("2021-03-11"), restaurant: "Sally's Deli" } ] )
集合中的某些文档缺少 score
字段。
如需填充缺失的 score
字段并确保数据中没有空白,请使用 $fill
。在以下示例中,$fill
使用 locf
填充方法,用序列中的前一个 score
值来填充缺失的 score
值:
db.restaurantReviewsMultiple.aggregate( [ { $fill: { sortBy: { date: 1 }, partitionBy: { "restaurant": "$restaurant" }, output: { "score": { method: "locf" } } } } ] )
在前面的管道中:
$fill
填充缺失的score
值。sortBy: { date: 1 }
按date
字段以升序排列文档,从最早到最晚。partitionBy: { "restaurant": "$restaurant" }
按restaurant
对数据进行分区。有两家餐厅:Joe's Pizza
和Sally's Deli
。输出指定:
score
作为要填写缺失值的字段。{ method: "locf" }
作为填充方法。locf
填充方法是用序列中上一次观察到的score
值来填充缺失的score
值。
示例输出:
[ { _id: ObjectId("620559f4394d47411658b58f"), date: ISODate("2021-03-08T00:00:00.000Z"), restaurant: "Joe's Pizza", score: 90 }, { _id: ObjectId("620559f4394d47411658b591"), date: ISODate("2021-03-09T00:00:00.000Z"), restaurant: "Joe's Pizza", score: 92 }, { _id: ObjectId("620559f4394d47411658b593"), date: ISODate("2021-03-10T00:00:00.000Z"), restaurant: "Joe's Pizza", score: 92 }, { _id: ObjectId("620559f4394d47411658b595"), date: ISODate("2021-03-11T00:00:00.000Z"), restaurant: "Joe's Pizza", score: 93 }, { _id: ObjectId("620559f4394d47411658b590"), date: ISODate("2021-03-08T00:00:00.000Z"), restaurant: "Sally's Deli", score: 75 }, { _id: ObjectId("620559f4394d47411658b592"), date: ISODate("2021-03-09T00:00:00.000Z"), restaurant: "Sally's Deli", score: 75 }, { _id: ObjectId("620559f4394d47411658b594"), date: ISODate("2021-03-10T00:00:00.000Z"), restaurant: "Sally's Deli", score: 68 }, { _id: ObjectId("620559f4394d47411658b596"), date: ISODate("2021-03-11T00:00:00.000Z"), restaurant: "Sally's Deli", score: 68 } ]
指明填充字段的方式是否为 $fill
填充缺失值时,输出结果不会显示该值是用 $fill
操作符填充的,还是该值最初就存在于文档中。为了区分已填充的值和已存在的值,可以在 $fill
前使用 $set
阶段,并根据值是否存在设置新字段。
例如,创建 restaurantReviews
集合,其中包含一家餐厅在一段时间内的评论得分:
db.restaurantReviews.insertMany( [ { date: ISODate("2021-03-08"), score: 90 }, { date: ISODate("2021-03-09"), score: 92 }, { date: ISODate("2021-03-10") }, { date: ISODate("2021-03-11") }, { date: ISODate("2021-03-12"), score: 85 }, { date: ISODate("2021-03-13") } ] )
集合中的某些文档缺失 score
字段。您可以使用 $fill
操作符填充缺失的 score
值。
创建管道来执行以下操作:
在每个文档中添加新字段(使用
$set
),说明文档的score
字段在$fill
操作符填充值之前是否存在。这个新字段名为valueExisted
。使用序列中最后观察到的
score
填充缺失的score
值。填充方法locf
代表“最后一次观测结转”。
管道是这样的:
db.restaurantReviews.aggregate( [ { $set: { "valueExisted": { "$ifNull": [ { "$toBool": { "$toString": "$score" } }, false ] } } }, { $fill: { sortBy: { date: 1 }, output: { "score": { method: "locf" } } } } ] )
注意
处理零值
输出:
[ { _id: ObjectId("63595116b1fac2ee2e957f15"), date: ISODate("2021-03-08T00:00:00.000Z"), score: 90, valueExisted: true }, { _id: ObjectId("63595116b1fac2ee2e957f16"), date: ISODate("2021-03-09T00:00:00.000Z"), score: 92, valueExisted: true }, { _id: ObjectId("63595116b1fac2ee2e957f17"), date: ISODate("2021-03-10T00:00:00.000Z"), valueExisted: false, score: 92 }, { _id: ObjectId("63595116b1fac2ee2e957f18"), date: ISODate("2021-03-11T00:00:00.000Z"), valueExisted: false, score: 92 }, { _id: ObjectId("63595116b1fac2ee2e957f19"), date: ISODate("2021-03-12T00:00:00.000Z"), score: 85, valueExisted: true }, { _id: ObjectId("63595116b1fac2ee2e957f1a"), date: ISODate("2021-03-13T00:00:00.000Z"), valueExisted: false, score: 85 } ]