

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# $setWindowFields
<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 }`遞減順序的形式排序每個分割區中的文件。當您使用文件型或範圍型視窗邊界，以及使用其中一個排名運算子時， `sortBy` 是必要的。
+ `output`：必要。指定要附加到每個文件的欄位的文件。您可以指定多個輸出欄位，但每個輸出欄位只需要一個視窗運算子、其引數，以及定義視窗邊界的選用`window`文件：`{ <field>: { <windowOperator>: <expression>, window: { ... } } }`。

## 視窗邊界
<a name="setWindowFields-bounds"></a>

在每個`output`欄位中，您可以選擇使用`window`文件定義視窗邊界。視窗可以依文件位置或值範圍來定義。
+ `documents`：將視窗指定為目前文件前後的文件數量，格式為 `[ <lowerBound>, <upperBound> ]`。每個邊界都是整數位移 （例如 `-1`或 `2`)，或其中一個關鍵字 `"unbounded"`和 `"current"`。當用作下限時， 會將視窗`"unbounded"`延伸到分割區的開頭，當用作上限時延伸到分割區的結尾。 `"current"`表示目前的文件。除非兩個邊界都是 ，`sortBy`否則`documents`視窗都需要 `"unbounded"`。
+ `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`、`$topN`、、 和 `$minN` `$maxN``$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()
```

------