Docs 菜单
Docs 主页
/
MongoDB Manual
/ / /

$fill(聚合)

在此页面上

  • 定义
  • 语法
  • 行为和限制
  • partitionByFields 限制
  • linear 行为
  • locf 行为
  • $fill 和聚合操作符的比较
  • 示例
  • 用常量值填充缺失字段值
  • 用线性插值填充缺失字段值
  • 根据上一次观察到的值填充缺失字段值
  • 为不同分区填充数据
  • 指明填充字段的方式是否为 $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

指定一个表达式对文档进行群组。 在$fill阶段,一群组文档称为分区

如果省略 partitionBypartitionByFields$fill 将使用整个集合的一个分区。

partitionBypartitionByFields 互斥。

请参阅示例

Optional

指定一个字段数组作为复合键,以便对文档进行分组。在 $fill 阶段,每组文档都称为一个分区

如果省略 partitionBypartitionByFields$fill 将使用整个集合的一个分区。

partitionBypartitionByFields 互斥。

请参阅 partitionByFields 限制。

如果在至少一个输出 <field> 中指定了方法,则为必填项。

否则为可选。

指定每个分区内用于对文档进行排序的字段。使用与 $sort 阶段相同的语法。

必需

指定对象,其中包含要填充缺失值的每个字段。您可以在输出对象中指定多个字段。

对象名称是要填写的字段名称。对象值指定了字段的填充方式。

必需

指定对象,指示如何填充目标字段中的缺失值。

对象名称必须为 valuemethod。如果名称为:

如果 partitionByFields 数组中有任何字段名称,则 $fill 会返回错误:

  • 求值为非字符串值。

  • $ 开头。

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 填充方法时,如果在单个分区的 sortBy 字段中有任何重复值,则 $fill 将返回错误。

有关使用 linear 填充方法的完整示例,请参阅使用线性插值填充缺失字段值。

locf 代表最后的观察结果。

  • 如果填写的字段包含 null 和非空值,locf 会根据 sortBy 排序将 null 和缺失值设置为该字段的最后一个已知非空值。

    • 如果该字段在分区中仅包含 null 或缺失值,locf 会将该分区的字段值设置为 null

    • null 而排序顺序中出现在非空值之前的缺失字段值仍为 null

  • 如需使用 locf 填充方法,还必须使用 sortBy 字段对数据排序。

有关使用 locf 填充方法的完整示例,请参阅根据上一次观察到的值填充缺失字段值

要填充文档中的 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 }
}
}
}
] )

在前面的管道中:

  • $fill 填充缺失字段的值。

  • 输出指定:

    • 要填充的字段名称。

    • 填充字段要设置的值。在此示例中,输出指定常量值为 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 值,请将 $filllinear 填充方法一起使用:

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 PizzaSally'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 前使用 $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" }
}
}
}
] )

注意

处理零值

$ifNull 表达式中,score 值被转换为字符串,然后再转换为布尔值。$toBool 表达式总是将字符串转换为 true。如果不将 score 值转换为字符串,0score 值会将 valueExisted 设置为 false

输出:

[
{
_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
}
]

后退

$facet