文档菜单
文档首页
/
MongoDB 手册
/ / /

$setWindowFields(聚合)

本页内容

  • 定义
  • 语法
  • 行为
  • 窗口操作符
  • 限制
  • 示例
$setWindowFields

新增在版本5.0.

对集合中指定范围的文档(称为窗口)执行操作,并基于所选窗口操作符.

例如,您可以使用$setWindowFields阶段来输出

  • 集合中两个文档之间的销售差异。

  • 销售排名。

  • 累计销售总额。

  • 分析复杂的时间序列信息,而无需将数据导出到外部数据库。

阶段$setWindowFields的语法

{
$setWindowFields: {
partitionBy: <expression>,
sortBy: {
<sort field 1>: <sort order>,
<sort field 2>: <sort order>,
...,
<sort field n>: <sort order>
},
output: {
<output field 1>: {
<window operator>: <window operator parameters>,
window: {
documents: [ <lower boundary>, <upper boundary> ],
range: [ <lower boundary>, <upper boundary> ],
unit: <time unit>
}
},
<output field 2>: { ... },
...
<output field n>: { ... }
}
}
}

阶段$setWindowFields接受包含以下字段的文档

字段
必需性
描述
可选

指定一个表达式来分组文档。在$setWindowFields阶段,文档组被称为分区。默认为整个集合的一个分区。

某些操作符必需(见限制)

指定在分区中对文档进行排序的字段。使用与$sort阶段相同的语法。默认不排序。

必需

指定要添加到$setWindowFields阶段返回的文档中的字段。每个字段都设置为窗口操作符返回的结果。

字段可以包含来指定嵌套文档字段和数组字段。在$setWindowFields阶段,嵌套文档点符号的语义与$addFields$set阶段相同。参见嵌套文档 $addFields 示例嵌套文档 $set 示例

可选

指定窗口边界和参数。窗口边界是包含的。默认为无界窗口,包含分区中的所有文档。

指定一个文档范围窗口。

可选

一个窗口,其上下边界相对于从集合中读取的当前文档位置进行指定。

窗口边界使用一个包含下限和上限字符串或整数的两个元素数组进行指定。使用

  • "current"字符串表示输出中的当前文档位置。

  • "unbounded"字符串表示分区中的第一个或最后一个文档位置。

  • 整数表示相对于当前文档的位置。使用负整数表示当前文档之前的位置。使用正整数表示当前文档之后的位置。0是当前文档位置。

请参阅文档窗口示例。

可选

一个窗口,其上下边界使用基于当前文档中sortBy字段的值范围进行定义。

窗口边界使用一个包含下限和上限字符串或数字的两个元素数组进行指定。使用

  • "current"字符串表示输出中的当前文档位置。

  • "unbounded"字符串表示分区中的第一个或最后一个文档位置。

  • 一个数字,将其添加到当前文档中sortBy字段的值。如果sortBy字段的值在上下边界内(包含边界),则文档在窗口内。

请参阅范围窗口示例。

可选

指定时间范围窗口边界的单位。可以设置为以下字符串之一

  • "year"

  • "quarter"

  • "month"

  • "week"

  • "day"

  • "hour"

  • "minute"

  • "second"

  • "millisecond"

如果省略,则默认使用数值范围窗口边界。

请参阅时间范围窗口示例。

提示

另请参阅

$setWindowFields 阶段会将新字段追加到现有文档中。您可以在聚合操作中包含一个或多个 $setWindowFields 阶段。

从 MongoDB 5.3 版本开始,您可以使用 $setWindowFields 阶段与 事务 以及 "snapshot" 读取关注点一起使用。

$setWindowFields 阶段不保证返回文档的顺序。

这些操作符可以与 $setWindowFields 阶段一起使用

针对 $setWindowFields 阶段的限制

  • 在 MongoDB 5.3 之前,无法使用 $setWindowFields 阶段

  • sortByRankorder 窗口操作符是必需的。

    • Rankorder 窗口操作符。

    • 有界窗口(无论是文档窗口还是范围窗口)。

    • $linearFill 操作符。

  • 范围窗口需要所有 sortBy 值都是数字。

  • 时间范围窗口需要所有 sortBy 值都是日期。

  • 范围和时间范围窗口只能包含一个 sortBy 字段,且排序必须是升序的。

  • 您不能同时指定文档窗口和范围窗口。

  • 这些操作符使用隐式窗口,如果指定了窗口选项,则返回错误。

  • 对于范围窗口,只有指定范围内的数字包含在窗口中。排除缺失、未定义和 null 值。

  • 对于时间范围窗口

    • 窗口中仅包含日期和时间类型。

    • 数值边界值必须是整数。例如,您可以使用2小时作为边界,但不能使用1.5小时。

  • 对于空窗口或不兼容值的窗口(例如,在字符串上使用$sum),返回值取决于运算符

    • 对于$count$sum,返回值是0

    • 对于$addToSet$push,返回值是一个空数组。

    • 对于所有其他运算符,返回值是null

创建一个包含加利福尼亚州(CA)和华盛顿州(WA)蛋糕销售的cakeSales集合

db.cakeSales.insertMany( [
{ _id: 0, type: "chocolate", orderDate: new Date("2020-05-18T14:10:30Z"),
state: "CA", price: 13, quantity: 120 },
{ _id: 1, type: "chocolate", orderDate: new Date("2021-03-20T11:30:05Z"),
state: "WA", price: 14, quantity: 140 },
{ _id: 2, type: "vanilla", orderDate: new Date("2021-01-11T06:31:15Z"),
state: "CA", price: 12, quantity: 145 },
{ _id: 3, type: "vanilla", orderDate: new Date("2020-02-08T13:13:23Z"),
state: "WA", price: 13, quantity: 104 },
{ _id: 4, type: "strawberry", orderDate: new Date("2019-05-18T16:09:01Z"),
state: "CA", price: 41, quantity: 162 },
{ _id: 5, type: "strawberry", orderDate: new Date("2019-01-08T06:12:03Z"),
state: "WA", price: 43, quantity: 134 }
] )

以下示例使用cakeSales集合。

此示例使用 文档 窗口在 $setWindowFields 中输出每个 state 的累计蛋糕销售 quantity

db.cakeSales.aggregate( [
{
$setWindowFields: {
partitionBy: "$state",
sortBy: { orderDate: 1 },
output: {
cumulativeQuantityForState: {
$sum: "$quantity",
window: {
documents: [ "unbounded", "current" ]
}
}
}
}
}
] )

在示例中

  • partitionBy: "$state" 根据 state 对集合中的文档进行 分区。有 CAWA 的分区。

  • sortBy: { orderDate: 1 } 对每个分区中的文档按 orderDate 进行 排序,升序(1),因此最早的 orderDate 是第一个。

  • output:

    • cumulativeQuantityForState 字段设置为每个 state 的累计 quantity,该值通过在分区中对前一个值进行连续添加而增加。

    • 使用在 $sum 操作符运行的 文档 窗口中计算累计 quantity

      窗口 包含一个无界的下限和当前文档之间的文档。这意味着 $sum 返回从分区开始到当前文档之间的文档的累计 quantity

在此示例输出中,显示在 cumulativeQuantityForState 字段中的 CAWA 的累计 quantity

{ "_id" : 4, "type" : "strawberry", "orderDate" : ISODate("2019-05-18T16:09:01Z"),
"state" : "CA", "price" : 41, "quantity" : 162, "cumulativeQuantityForState" : 162 }
{ "_id" : 0, "type" : "chocolate", "orderDate" : ISODate("2020-05-18T14:10:30Z"),
"state" : "CA", "price" : 13, "quantity" : 120, "cumulativeQuantityForState" : 282 }
{ "_id" : 2, "type" : "vanilla", "orderDate" : ISODate("2021-01-11T06:31:15Z"),
"state" : "CA", "price" : 12, "quantity" : 145, "cumulativeQuantityForState" : 427 }
{ "_id" : 5, "type" : "strawberry", "orderDate" : ISODate("2019-01-08T06:12:03Z"),
"state" : "WA", "price" : 43, "quantity" : 134, "cumulativeQuantityForState" : 134 }
{ "_id" : 3, "type" : "vanilla", "orderDate" : ISODate("2020-02-08T13:13:23Z"),
"state" : "WA", "price" : 13, "quantity" : 104, "cumulativeQuantityForState" : 238 }
{ "_id" : 1, "type" : "chocolate", "orderDate" : ISODate("2021-03-20T11:30:05Z"),
"state" : "WA", "price" : 14, "quantity" : 140, "cumulativeQuantityForState" : 378 }

此示例使用 documents 窗口在 $setWindowFields 中输出 orderDate 中每年的累积蛋糕销量 quantity

db.cakeSales.aggregate( [
{
$setWindowFields: {
partitionBy: { $year: "$orderDate" },
sortBy: { orderDate: 1 },
output: {
cumulativeQuantityForYear: {
$sum: "$quantity",
window: {
documents: [ "unbounded", "current" ]
}
}
}
}
}
] )

在示例中

  • partitionBy: { $year: "$orderDate" } 根据 orderDate 中的 $year 将集合中的文档进行 $year 分区。存在 201920202021 分区。

  • sortBy: { orderDate: 1 } 对每个分区中的文档按 orderDate 进行 排序,升序(1),因此最早的 orderDate 是第一个。

  • output:

    • cumulativeQuantityForYear 字段设置为每年累积的 quantity,该值在分区中按顺序添加到前一个值。

    • 使用在 $sum 操作符运行的 文档 窗口中计算累计 quantity

      窗口 包含一个无界的下限和当前文档之间的文档。这意味着 $sum 返回从分区开始到当前文档之间的文档的累计 quantity

在此示例输出中,每年的累积 quantity 显示在 cumulativeQuantityForYear 字段中

{ "_id" : 5, "type" : "strawberry", "orderDate" : ISODate("2019-01-08T06:12:03Z"),
"state" : "WA", "price" : 43, "quantity" : 134, "cumulativeQuantityForYear" : 134 }
{ "_id" : 4, "type" : "strawberry", "orderDate" : ISODate("2019-05-18T16:09:01Z"),
"state" : "CA", "price" : 41, "quantity" : 162, "cumulativeQuantityForYear" : 296 }
{ "_id" : 3, "type" : "vanilla", "orderDate" : ISODate("2020-02-08T13:13:23Z"),
"state" : "WA", "price" : 13, "quantity" : 104, "cumulativeQuantityForYear" : 104 }
{ "_id" : 0, "type" : "chocolate", "orderDate" : ISODate("2020-05-18T14:10:30Z"),
"state" : "CA", "price" : 13, "quantity" : 120, "cumulativeQuantityForYear" : 224 }
{ "_id" : 2, "type" : "vanilla", "orderDate" : ISODate("2021-01-11T06:31:15Z"),
"state" : "CA", "price" : 12, "quantity" : 145, "cumulativeQuantityForYear" : 145 }
{ "_id" : 1, "type" : "chocolate", "orderDate" : ISODate("2021-03-20T11:30:05Z"),
"state" : "WA", "price" : 14, "quantity" : 140, "cumulativeQuantityForYear" : 285 }

此示例使用 文档 窗口在 $setWindowFields 中输出蛋糕销售 数量 的移动平均值。

db.cakeSales.aggregate( [
{
$setWindowFields: {
partitionBy: { $year: "$orderDate" },
sortBy: { orderDate: 1 },
output: {
averageQuantity: {
$avg: "$quantity",
window: {
documents: [ -1, 0 ]
}
}
}
}
}
] )

在示例中

  • partitionBy: "$orderDate" 根据 orderDate 中的 $year 将集合中的文档进行分区。存在 201920202021 的分区。

  • sortBy: { orderDate: 1 } 对每个分区中的文档按 orderDate 进行 排序,升序(1),因此最早的 orderDate 是第一个。

  • output:

    • averageQuantity 字段设置为每年 数量 的移动平均值。

    • 使用在 文档 窗口中运行的 $avg 操作符计算移动平均 数量

      窗口 包含介于 -10 之间的文档。这意味着 $avg 返回分区中当前文档之前的文档(-1)和当前文档(0)之间的移动平均 数量

在此示例输出中,移动平均 数量 显示在 averageQuantity 字段中。

{ "_id" : 5, "type" : "strawberry", "orderDate" : ISODate("2019-01-08T06:12:03Z"),
"state" : "WA", "price" : 43, "quantity" : 134, "averageQuantity" : 134 }
{ "_id" : 4, "type" : "strawberry", "orderDate" : ISODate("2019-05-18T16:09:01Z"),
"state" : "CA", "price" : 41, "quantity" : 162, "averageQuantity" : 148 }
{ "_id" : 3, "type" : "vanilla", "orderDate" : ISODate("2020-02-08T13:13:23Z"),
"state" : "WA", "price" : 13, "quantity" : 104, "averageQuantity" : 104 }
{ "_id" : 0, "type" : "chocolate", "orderDate" : ISODate("2020-05-18T14:10:30Z"),
"state" : "CA", "price" : 13, "quantity" : 120, "averageQuantity" : 112 }
{ "_id" : 2, "type" : "vanilla", "orderDate" : ISODate("2021-01-11T06:31:15Z"),
"state" : "CA", "price" : 12, "quantity" : 145, "averageQuantity" : 145 }
{ "_id" : 1, "type" : "chocolate", "orderDate" : ISODate("2021-03-20T11:30:05Z"),
"state" : "WA", "price" : 14, "quantity" : 140, "averageQuantity" : 142.5 }

此示例使用 文档 窗口在 $setWindowFields 中输出每个 orderDate$year 的累积和最大蛋糕销售 数量 值。

db.cakeSales.aggregate( [
{
$setWindowFields: {
partitionBy: { $year: "$orderDate" },
sortBy: { orderDate: 1 },
output: {
cumulativeQuantityForYear: {
$sum: "$quantity",
window: {
documents: [ "unbounded", "current" ]
}
},
maximumQuantityForYear: {
$max: "$quantity",
window: {
documents: [ "unbounded", "unbounded" ]
}
}
}
}
}
] )

在示例中

  • partitionBy: "$orderDate" 根据 orderDate 中的 $year 将集合中的文档进行分区。存在 201920202021 的分区。

  • sortBy: { orderDate: 1 } 对每个分区中的文档按 orderDate 进行 排序,升序(1),因此最早的 orderDate 是第一个。

  • output:

    • cumulativeQuantityForYear 字段设置为每年的累积 数量

    • 使用在 $sum 操作符运行的 文档 窗口中计算累计 quantity

      窗口 包含一个 无界 的下限和当前文档。这意味着 $sum 返回从分区开始到当前文档之间的累积数量。

    • maximumQuantityForYear 字段设置为每年的最大 数量

    • 使用 $max 操作符,在 文档 窗口中计算所有文档的最大 数量

      窗口 包含一个从 无界 下限到 上界 的文档。这意味着 $max 返回分区中文档的最大数量。

在此示例输出中,累积 数量 显示在 cumulativeQuantityForYear 字段中,最大 数量 显示在 maximumQuantityForYear 字段中

{ "_id" : 5, "type" : "strawberry", "orderDate" : ISODate("2019-01-08T06:12:03Z"),
"state" : "WA", "price" : 43, "quantity" : 134,
"cumulativeQuantityForYear" : 134, "maximumQuantityForYear" : 162 }
{ "_id" : 4, "type" : "strawberry", "orderDate" : ISODate("2019-05-18T16:09:01Z"),
"state" : "CA", "price" : 41, "quantity" : 162,
"cumulativeQuantityForYear" : 296, "maximumQuantityForYear" : 162 }
{ "_id" : 3, "type" : "vanilla", "orderDate" : ISODate("2020-02-08T13:13:23Z"),
"state" : "WA", "price" : 13, "quantity" : 104,
"cumulativeQuantityForYear" : 104, "maximumQuantityForYear" : 120 }
{ "_id" : 0, "type" : "chocolate", "orderDate" : ISODate("2020-05-18T14:10:30Z"),
"state" : "CA", "price" : 13, "quantity" : 120,
"cumulativeQuantityForYear" : 224, "maximumQuantityForYear" : 120 }
{ "_id" : 2, "type" : "vanilla", "orderDate" : ISODate("2021-01-11T06:31:15Z"),
"state" : "CA", "price" : 12, "quantity" : 145,
"cumulativeQuantityForYear" : 145, "maximumQuantityForYear" : 145 }
{ "_id" : 1, "type" : "chocolate", "orderDate" : ISODate("2021-03-20T11:30:05Z"),
"state" : "WA", "price" : 14, "quantity" : 140,
"cumulativeQuantityForYear" : 285, "maximumQuantityForYear" : 145 }

此示例使用 范围 窗口在 $setWindowFields 中返回当前文档 价格 值加减 10 美元的订单所售蛋糕的 数量 值总和。

db.cakeSales.aggregate( [
{
$setWindowFields: {
partitionBy: "$state",
sortBy: { price: 1 },
output: {
quantityFromSimilarOrders: {
$sum: "$quantity",
window: {
range: [ -10, 10 ]
}
}
}
}
}
] )

在示例中

  • partitionBy: "$state" 根据 state 对集合中的文档进行 分区。有 CAWA 的分区。

  • sortBy: { price: 1 } 价格 递增顺序(1)对每个分区的文档进行排序,因此最低 价格 最先。

  • outputquantityFromSimilarOrders 字段设置为从 范围 窗口中的文档中 数量 值的总和。

    • 窗口 包含从下限 -10 到上限 10 的文档。范围是包含的。

    • $sum 返回包含在当前文档 价格 值加减 10 美元范围内的 数量 值总和。

在此示例输出中,窗口中文档的 数量 值总和显示在 quantityFromSimilarOrders 字段中

{ "_id" : 2, "type" : "vanilla", "orderDate" : ISODate("2021-01-11T06:31:15Z"),
"state" : "CA", "price" : 12, "quantity" : 145, "quantityFromSimilarOrders" : 265 }
{ "_id" : 0, "type" : "chocolate", "orderDate" : ISODate("2020-05-18T14:10:30Z"),
"state" : "CA", "price" : 13, "quantity" : 120, "quantityFromSimilarOrders" : 265 }
{ "_id" : 4, "type" : "strawberry", "orderDate" : ISODate("2019-05-18T16:09:01Z"),
"state" : "CA", "price" : 41, "quantity" : 162, "quantityFromSimilarOrders" : 162 }
{ "_id" : 3, "type" : "vanilla", "orderDate" : ISODate("2020-02-08T13:13:23Z"),
"state" : "WA", "price" : 13, "quantity" : 104, "quantityFromSimilarOrders" : 244 }
{ "_id" : 1, "type" : "chocolate", "orderDate" : ISODate("2021-03-20T11:30:05Z"),
"state" : "WA", "price" : 14, "quantity" : 140, "quantityFromSimilarOrders" : 244 }
{ "_id" : 5, "type" : "strawberry", "orderDate" : ISODate("2019-01-08T06:12:03Z"),
"state" : "WA", "price" : 43, "quantity" : 134, "quantityFromSimilarOrders" : 134 }

以下示例使用了一个具有正上界时间范围单位的时间范围窗口窗口,在单位$setWindowFields。管道输出每个匹配指定时间范围的stateorderDate值数组。

db.cakeSales.aggregate( [
{
$setWindowFields: {
partitionBy: "$state",
sortBy: { orderDate: 1 },
output: {
recentOrders: {
$push: "$orderDate",
window: {
range: [ "unbounded", 10 ],
unit: "month"
}
}
}
}
}
] )

在示例中

  • partitionBy: "$state" 根据 state 对集合中的文档进行 分区。有 CAWA 的分区。

  • sortBy: { orderDate: 1 } 对每个分区中的文档按 orderDate 进行 排序,升序(1),因此最早的 orderDate 是第一个。

  • output:

在本例输出中,显示的是orderDate数组,该数组对应于CAWA,并在recentOrders字段中展示。

{ "_id" : 4, "type" : "strawberry", "orderDate" : ISODate("2019-05-18T16:09:01Z"),
"state" : "CA", "price" : 41, "quantity" : 162,
"recentOrders" : [ ISODate("2019-05-18T16:09:01Z") ] }
{ "_id" : 0, "type" : "chocolate", "orderDate" : ISODate("2020-05-18T14:10:30Z"),
"state" : "CA", "price" : 13, "quantity" : 120,
"recentOrders" : [ ISODate("2019-05-18T16:09:01Z"), ISODate("2020-05-18T14:10:30Z"), ISODate("2021-01-11T06:31:15Z") ] }
{ "_id" : 2, "type" : "vanilla", "orderDate" : ISODate("2021-01-11T06:31:15Z"),
"state" : "CA", "price" : 12, "quantity" : 145,
"recentOrders" : [ ISODate("2019-05-18T16:09:01Z"), ISODate("2020-05-18T14:10:30Z"), ISODate("2021-01-11T06:31:15Z") ] }
{ "_id" : 5, "type" : "strawberry", "orderDate" : ISODate("2019-01-08T06:12:03Z"),
"state" : "WA", "price" : 43, "quantity" : 134,
"recentOrders" : [ ISODate("2019-01-08T06:12:03Z") ] }
{ "_id" : 3, "type" : "vanilla", "orderDate" : ISODate("2020-02-08T13:13:23Z"),
"state" : "WA", "price" : 13, "quantity" : 104,
"recentOrders" : [ ISODate("2019-01-08T06:12:03Z"), ISODate("2020-02-08T13:13:23Z") ] }
{ "_id" : 1, "type" : "chocolate", "orderDate" : ISODate("2021-03-20T11:30:05Z"),
"state" : "WA", "price" : 14, "quantity" : 140,
"recentOrders" : [ ISODate("2019-01-08T06:12:03Z"), ISODate("2020-02-08T13:13:23Z"), ISODate("2021-03-20T11:30:05Z") ] }

以下示例使用了一个带有负上限时间范围窗口和一个时间范围单位$setWindowFields。该管道输出一个数组,包含每个匹配指定时间范围的stateorderDate值。

db.cakeSales.aggregate( [
{
$setWindowFields: {
partitionBy: "$state",
sortBy: { orderDate: 1 },
output: {
recentOrders: {
$push: "$orderDate",
window: {
range: [ "unbounded", -10 ],
unit: "month"
}
}
}
}
}
] )

在示例中

  • partitionBy: "$state" 根据 state 对集合中的文档进行 分区。有 CAWA 的分区。

  • sortBy: { orderDate: 1 } 对每个分区中的文档按 orderDate 进行 排序,升序(1),因此最早的 orderDate 是第一个。

  • output:

  • 该窗口包含从unbounded下限到设置为-10(当前文档的orderDate值10个月前)的上限之间的文档,使用时间范围单位

  • $push返回从分区开始到当前文档的orderDate值减去10个月的文档的orderDate值的数组。

在本例输出中,显示的是orderDate数组,该数组对应于CAWA,并在recentOrders字段中展示。

{ "_id" : 4, "type" : "strawberry", "orderDate" : ISODate("2019-05-18T16:09:01Z"),
"state" : "CA", "price" : 41, "quantity" : 162,
"recentOrders" : [ ] }
{ "_id" : 0, "type" : "chocolate", "orderDate" : ISODate("2020-05-18T14:10:30Z"),
"state" : "CA", "price" : 13, "quantity" : 120,
"recentOrders" : [ ISODate("2019-05-18T16:09:01Z") ] }
{ "_id" : 2, "type" : "vanilla", "orderDate" : ISODate("2021-01-11T06:31:15Z"),
"state" : "CA", "price" : 12, "quantity" : 145,
"recentOrders" : [ ISODate("2019-05-18T16:09:01Z") ] }
{ "_id" : 5, "type" : "strawberry", "orderDate" : ISODate("2019-01-08T06:12:03Z"),
"state" : "WA", "price" : 43, "quantity" : 134,
"recentOrders" : [ ] }
{ "_id" : 3, "type" : "vanilla", "orderDate" : ISODate("2020-02-08T13:13:23Z"),
"state" : "WA", "price" : 13, "quantity" : 104,
"recentOrders" : [ ISODate("2019-01-08T06:12:03Z") ] }
{ "_id" : 1, "type" : "chocolate", "orderDate" : ISODate("2021-03-20T11:30:05Z"),
"state" : "WA", "price" : 14, "quantity" : 140,
"recentOrders" : [ ISODate("2019-01-08T06:12:03Z"), ISODate("2020-02-08T13:13:23Z") ] }

提示

另请参阅

关于其他示例,请参阅IOT功耗,请参阅实用MongoDB聚合电子书。

返回

$set