

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# $set WindowFields
<a name="setWindowFields"></a>

8.0.2 版中的新增内容。

Amazon DocumentDB 中的`$setWindowFields`阶段对分区中指定范围的文档（*窗口*）执行操作，并根据所选的窗口运算符返回结果。该阶段将输入文档分成多个分区，可以选择对它们进行排序，并针对每个文档在周围文档的窗口上计算一个或多个输出字段。

**语法**

```
{
  $setWindowFields: {
    partitionBy: <expression>,
    sortBy: { <sortField1>: <sortOrder>, ... },
    output: {
      <outputField1>: {
        <windowOperator>: <expression>,
        window: {
          documents: [ <lowerBound>, <upperBound> ],
          range: [ <lowerBound>, <upperBound> ],
          unit: <time unit>
        }
      },
      ...
    }
  }
}
```

**参数**
+ `partitionBy`：可选。用于将文档分组为分区的单个表达式。这可以是字段路径（例如`"$state"`）或其他表达式。如果省略，则所有输入文档都属于一个分区。
+ `sortBy`：可选。一种文档，它以升序或降序的形式指定对每个分区中的文档`{ <field>: 1 }`进行排序所依据的一个或`{ <field>: -1 }`多个字段。当您使用基于文档或基于范围的窗口边界以及使用其中一个排名运算符时，A `sortBy` 是必填项。
+ `output`：必需。一个文档，它指定要附加到每个文档的字段。您可以指定多个输出字段，但每个输出字段只需要一个窗口运算符、其参数和一个定义窗口边界的可选`window`文档：`{ <field>: { <windowOperator>: <expression>, window: { ... } } }`。

## 窗口边界
<a name="setWindowFields-bounds"></a>

在每个`output`字段中，您可以选择使用`window`文档定义窗口边界。窗口可以通过文档位置或一系列值来定义。
+ `documents`：将窗口指定为表单中当前文档前后的多个文档`[ <lowerBound>, <upperBound> ]`。每个边界都是一个整数偏移量（例如，`-1`或`2`），或关键字`"unbounded"`和之一`"current"`。`"unbounded"`当用作下限时，将窗口扩展到分区的起点；当用作上限时，将窗口扩展到分区的末尾。`"current"`指当前文档。除非两个边界都是`"unbounded"`，`sortBy`否则`documents`窗口需要一个。
+ `range`：在表单中将窗口指定为与当前文档相关的`sortBy`字段值范围`[ <lowerBound>, <upperBound> ]`。每个边界都是与当前文档或`"unbounded"`关键字`sortBy`值相加的数字。一个`range`窗口只需要一个升序`sortBy`字段，并且不接受该`"current"`关键字。
+ `unit`：可选。与一起使用`range`将数字窗口边界解释为`sortBy`字段保存日期值的时间跨度。Amazon DocumentDB 支持以下单位：`"year"``"quarter"``"month"`、`"week"`、`"day"`、`"hour"`、`"minute"`、`"second"`、和`"millisecond"`。指定`unit`时，`range`边界必须为整数。

省略`window`文档时，默认窗口为`documents: ["unbounded", "unbounded"]`，它跨越整个分区。

**注意**  
当您使用超过日期值的`range`窗口时，我们建议使用固定持续时间，`unit`例如`"day"`（或更小）而不是`"month"``"quarter"``"year"`、或，后者与日历相关的长度会产生不明确的窗口边界。

## 支持的窗口运算符
<a name="setWindowFields-operators"></a>

Amazon DocumentDB 支持该`$setWindowFields`阶段`output`领域的以下 26 个窗口操作员：
+ `$sum`
+ `$avg`
+ `$count`
+ `$push`
+ `$addToSet`
+ `$stdDevPop`
+ `$stdDevSamp`
+ `$covariancePop`
+ `$covarianceSamp`
+ `$first`
+ `$last`
+ `$firstN`
+ `$lastN`
+ `$top`
+ `$bottom`
+ `$topN`
+ `$bottomN`
+ `$min`
+ `$minN`
+ `$max`
+ `$maxN`
+ `$median`
+ `$percentile`
+ `$rank`
+ `$denseRank`
+ `$documentNumber`

**注意**  
该`$setWindowFields`阶段的窗口`$rand`运算符不支持该运算符。

**空窗户 **

当一个窗口不包含任何文档时（例如，一个在范围内没有值的`range`窗口），结果取决于运算符：`$count`并`$sum`返回`0`；`$push`、、、、`$addToSet`、`$firstN`、`$lastN``$minN``$maxN``$topN`、，并`$bottomN`返回一个空数组；`$percentile`返回一个`null`值数组（每个请求的百分位数一个）；所有其他运算符返回`null`。

## 示例（MongoDB Shell）
<a name="setWindowFields-examples"></a>

以下示例按对文档进行分区`region`，按分区排序`day`，并计算`revenue`从分区起点到当前文档的运行总数。

**创建示例文档 **

```
db.sales.insertMany([
  { _id: 1, region: "east", day: 1, revenue: 100 },
  { _id: 2, region: "east", day: 2, revenue: 150 },
  { _id: 3, region: "east", day: 3, revenue: 120 },
  { _id: 4, region: "west", day: 1, revenue: 200 },
  { _id: 5, region: "west", day: 2, revenue: 180 }
]);
```

**查询示例 **

```
db.sales.aggregate([
  {
    $setWindowFields: {
      partitionBy: "$region",
      sortBy: { day: 1 },
      output: {
        runningRevenue: {
          $sum: "$revenue",
          window: { documents: ["unbounded", "current"] }
        }
      }
    }
  }
]);
```

**输出**

```
[
  { "_id": 1, "region": "east", "day": 1, "revenue": 100, "runningRevenue": 100 },
  { "_id": 2, "region": "east", "day": 2, "revenue": 150, "runningRevenue": 250 },
  { "_id": 3, "region": "east", "day": 3, "revenue": 120, "runningRevenue": 370 },
  { "_id": 4, "region": "west", "day": 1, "revenue": 200, "runningRevenue": 200 },
  { "_id": 5, "region": "west", "day": 2, "revenue": 180, "runningRevenue": 380 }
]
```

每个文档都添加了`runningRevenue`其分区`revenue`内直到包括当前文档在内的累积总和。

## 代码示例
<a name="setWindowFields-code"></a>

要查看使用该`$setWindowFields`阶段的代码示例，请选择要使用的语言的选项卡：

------
#### [ Node.js ]

```
const { MongoClient } = require('mongodb');

async function example() {
  const client = new MongoClient('mongodb://<username>:<password>@<cluster-endpoint>:27017/?tls=true&tlsCAFile=global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false');

  try {
    await client.connect();
    const db = client.db('test');
    const sales = db.collection('sales');

    await sales.insertMany([
      { _id: 1, region: "east", day: 1, revenue: 100 },
      { _id: 2, region: "east", day: 2, revenue: 150 },
      { _id: 3, region: "east", day: 3, revenue: 120 },
      { _id: 4, region: "west", day: 1, revenue: 200 },
      { _id: 5, region: "west", day: 2, revenue: 180 }
    ]);

    const result = await sales.aggregate([
      {
        $setWindowFields: {
          partitionBy: "$region",
          sortBy: { day: 1 },
          output: {
            runningRevenue: {
              $sum: "$revenue",
              window: { documents: ["unbounded", "current"] }
            }
          }
        }
      }
    ]).toArray();

    console.log(result);
  } finally {
    await client.close();
  }
}

example();
```

------
#### [ Python ]

```
from pymongo import MongoClient

def example():
    client = MongoClient('mongodb://<username>:<password>@<cluster-endpoint>:27017/?tls=true&tlsCAFile=global-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false')

    try:
        db = client['test']
        sales = db['sales']

        sales.insert_many([
            { '_id': 1, 'region': 'east', 'day': 1, 'revenue': 100 },
            { '_id': 2, 'region': 'east', 'day': 2, 'revenue': 150 },
            { '_id': 3, 'region': 'east', 'day': 3, 'revenue': 120 },
            { '_id': 4, 'region': 'west', 'day': 1, 'revenue': 200 },
            { '_id': 5, 'region': 'west', 'day': 2, 'revenue': 180 }
        ])

        result = list(sales.aggregate([
            {
                '$setWindowFields': {
                    'partitionBy': '$region',
                    'sortBy': { 'day': 1 },
                    'output': {
                        'runningRevenue': {
                            '$sum': '$revenue',
                            'window': { 'documents': ['unbounded', 'current'] }
                        }
                    }
                }
            }
        ]))

        print(result)
    finally:
        client.close()

example()
```

------