

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

# 功能差異：Amazon DocumentDB 和 MongoDB
<a name="functional-differences"></a>

以下是 Amazon DocumentDB （與 MongoDB 相容） 和 MongoDB 之間的功能差異。

**Topics**
+ [Amazon DocumentDB 的功能優勢](#functional-differences.functional-benefits)
+ [已更新功能差異](#functional-differences.updated-functional-differences)
+ [MongoDB 的功能差異](#functional-differences.with-mongodb)

## Amazon DocumentDB 的功能優勢
<a name="functional-differences.functional-benefits"></a>

### 隱含交易
<a name="functional-differences.implicit-transactions"></a>

在 Amazon DocumentDB 中，所有 CRUD `update`陳述式 (`findAndModify`、`insert`、、`delete`) 保證原子性和一致性，即使是修改多個文件的操作也一樣。隨著 Amazon DocumentDB 4.0 的推出，現在支援為多陳述式和多集合操作提供 ACID 屬性的明確交易。如需在 Amazon DocumentDB 中使用交易的詳細資訊，請參閱 [Amazon DocumentDB 中的交易](transactions.md)。

以下是 Amazon DocumentDB 中的操作範例，可修改滿足原子和一致行為的多個文件。

```
db.miles.update(
    { "credit_card": { $eq: true } },
    { $mul: { "flight_miles.$[]": NumberInt(2) } },
    { multi: true }
)
```

```
db.miles.updateMany(
    { "credit_card": { $eq: true } }, 
    { $mul: { "flight_miles.$[]": NumberInt(2) } }
)
```

```
db.runCommand({
  update: "miles",
  updates: [
    {
      q: { "credit_card": { $eq: true } },
      u: { $mul: { "flight_miles.$[]": NumberInt(2) } },
      multi: true
    }
  ]
})
```

```
db.products.deleteMany({
  "cost": { $gt: 30.00 }
})
```

```
db.runCommand({
  delete: "products",
  deletes: [{ q: { "cost": { $gt: 30.00 } }, limit: 0 }]
})
```

構成 `updateMany` 和 `deleteMany` 之類批量操作的個別操作是不可部分完成，但整個批量操作是可部分完成。例如，如果個別插入操作成功執行而沒有錯誤，則整個 `insertMany` 操作都是不可部分完成。如果 `insertMany` 操作遇到錯誤，則 `insertMany` 操作內的每個單獨插入陳述式都將當作不可部分完成操作來執行。如果您需要 `insertMany`、 `updateMany`和 `deleteMany`操作的 ACID 屬性，建議您使用交易。

## 已更新功能差異
<a name="functional-differences.updated-functional-differences"></a>

Amazon DocumentDB 透過從客戶要求我們建置的功能向後工作，持續改善與 MongoDB 的相容性。本節包含我們在 Amazon DocumentDB 中移除的功能差異，讓客戶更輕鬆地遷移和建置應用程式。

**Topics**
+ [陣列索引](#functional-differences.array-indexing)
+ [多金鑰索引](#functional-differences.multi-key-indexes)
+ [字串中的 Null 字元](#functional-differences.strings)
+ [角色類型存取控制](#functional-differences.role_based_access_control)
+ [`$regex` 索引](#functional-differences.regex-indexing)
+ [巢狀文件的投影](#functional-differences.nested-docs)

### 陣列索引
<a name="functional-differences.array-indexing"></a>

截至 2020 年 4 月 23 日，Amazon DocumentDB 現在支援為大於 2，048 個位元組的陣列編製索引。陣列中個別項目的限制仍為 2，048 個位元組，與 MongoDB 一致。

如果您要建立新索引，則不需要採取任何動作來利用改進的功能。如果您有現有的索引，您可以刪除索引再重新建立，以利用改進的功能。具有改進功能的最新索引版本為 `"v" : 3`。

**注意**  
對於生產叢集，刪除索引可能會影響您的應用程式效能。建議您在對生產系統變更時先加以測試並小心進行。此外，重新建立索引所需的時間，將是集合整體資料大小的函數。

您可以使用以下命令查詢索引的版本。

```
db.collection.getIndexes()
```

此操作的輸出將會如下所示。在此輸出中，索引的版本是 `"v" : 3`，這是最新的索引版本。

```
[
    {
        "v" : 3,
        "key" : {
        "_id" : 1
        },
        "name" : "_id_",
        "ns" : "test.test"
    }
]
```

### 多金鑰索引
<a name="functional-differences.multi-key-indexes"></a>

自 2020 年 4 月 23 日起，Amazon DocumentDB 現在支援在相同陣列中建立具有多個索引鍵的複合索引。

如果您要建立新索引，則不需要採取任何動作來利用改進的功能。如果您有現有的索引，您可以刪除索引再重新建立，以利用改進的功能。具有改進功能的最新索引版本為 `"v" : 3`。

**注意**  
對於生產叢集，刪除索引可能會影響您的應用程式效能。建議您在對生產系統變更時先加以測試並小心進行。此外，重新建立索引所需的時間，將是集合整體資料大小的函數。

您可以使用以下命令查詢索引的版本。

```
db.collection.getIndexes()
```

此操作的輸出將會如下所示。在此輸出中，索引的版本是 `"v" : 3`，這是最新的索引版本。

```
[
    {
        "v" : 3,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "test.test"
    }
]
```

### 字串中的 Null 字元
<a name="functional-differences.strings"></a>

自 2020 年 6 月 22 日起，Amazon DocumentDB 現在支援字串中的 null 字元 `'\0'` ()。

### 角色類型存取控制
<a name="functional-differences.role_based_access_control"></a>

截至 2020 年 3 月 26 日，Amazon DocumentDB 支援內建角色的角色型存取控制 (RBAC)。如需詳細資訊，請參閱 [以角色為基礎的存取控制](role_based_access_control.md)。

### `$regex` 索引
<a name="functional-differences.regex-indexing"></a>

自 2020 年 6 月 22 日起，Amazon DocumentDB 現在支援`$regex`運算子利用索引的能力。

若要使用與 `$regex` 運算子的索引，您必須使用 `hint()` 命令。使用 `hint()` 時，您必須指定要套用 `$regex` 之欄位的名稱。例如，如果您在欄位 `product` 有名為 `p_1` 的索引，`db.foo.find({product: /^x.*/}).hint({product:1})` 將使用 `p_1` 索引，但 `db.foo.find({product: /^x.*/}).hint(“p_1”)` 不會使用該索引。您可以使用 `explain()` 命令或使用分析工具的記錄慢速查詢功能來驗證是否已選擇索引。例如 `db.foo.find({product: /^x.*/}).hint(“p_1”).explain()`。

**注意**  
`hint()` 方法一次只能與一個索引一起使用。

`$regex` 查詢的索引使用已針對使用字首且未指定 `i`, 、`m` 或 `o` 規則運算式選項的查詢進行最佳化。

將 `$regex` 搭配索引使用時，建議您在有高度選擇性的欄位上建立索引，其中重複值的數目會小於集合中文件總數的 1%。例如，如果您的集合包含 100,000 份文件，則只在相同值出現 1000 次或更少的欄位上建立索引。

### 巢狀文件的投影
<a name="functional-differences.nested-docs"></a>

Amazon DocumentDB `$project` 和 MongoDB 在 3.6 版中有功能差異，已在 Amazon DocumentDB 4.0 中解決，但在 Amazon DocumentDB 3.6 中仍不受支援。

Amazon DocumentDB 3.6 只會在套用投影時考慮巢狀文件中的第一個欄位，而 MongoDB 3.6 也會剖析子文件，並將投影套用至每個子文件。

例如：如果投影為 `“a.b.c”: 1`，則行為在 Amazon DocumentDB 和 MongoDB 中都如預期般運作。不過，如果投影為 `{a:{b:{c:1}}}`，則 Amazon DocumentDB 3.6 只會將投影套用至 `a` ，而不是 `b`或 `c`。在 Amazon DocumentDB 4.0 中，投影`{a:{b:{c:1}}}`將套用至 `a`、 `b`和 `c`。

## MongoDB 的功能差異
<a name="functional-differences.with-mongodb"></a>

**Topics**
+ [`$vectorSearch`運算子](#functional-differences.vector-search)
+ [`OpCountersCommand`](#functional-differences.op-counter)
+ [管理員資料庫和集合](#functional-differences.admin-databases)
+ [`cursormaxTimeMS`](#functional-differences.cursormaxTimeMS)
+ [explain()](#functional-differences.explain)
+ [索引組建](#functional-differences.background-indexes)
+ [路徑中具有空索引鍵的查詢](#functional-differences.lookup-empty)
+ [MongoDB APIs、操作和資料類型](#functional-differences.mongo-apis)
+ [`mongodump` 和 `mongorestore` 公用程式](#functional-differences.mongodump-mongorestore)
+ [結果排序](#functional-differences.result-ordering)
+ [可重試的寫入](#functional-differences.retryable-writes)
+ [稀鬆索引](#functional-differences.sparse-index)
+ [在`$all`表達式`$elemMatch`內使用](#functional-differences.elemMatch)
+ [欄位名稱中的 Dollar(\$1) 和 dot(.)](#functional-differences-dollardot)
+ [`$lookup`](#functional-differences.lookup)
+ [`$natural` 和反向排序](#functional-differences.natural)

### `$vectorSearch`運算子
<a name="functional-differences.vector-search"></a>

Amazon DocumentDB 不支援 `$vectorSearch` 做為獨立運算子。相反地，我們在`$search`運算子`vectorSearch`內部支援 。如需詳細資訊，請參閱[Amazon DocumentDB 的向量搜尋](vector-search.md)。

### `OpCountersCommand`
<a name="functional-differences.op-counter"></a>

Amazon DocumentDB  `OpCountersCommand`的行為偏離 MongoDB 的行為`opcounters.command` ，如下所示：
+ MongoDB `opcounters.command`計算所有命令，但插入、更新和刪除除外，而 Amazon DocumentDB 的 `OpCountersCommand` 也排除命令`find`。
+ Amazon DocumentDB 會將一些內部命令計入 `OpCountersCommand`。

### 管理員資料庫和集合
<a name="functional-differences.admin-databases"></a>

Amazon DocumentDB 不支援管理員或本機資料庫，也不支援 MongoDB `system.*`或`startup_log`集合。

### `cursormaxTimeMS`
<a name="functional-differences.cursormaxTimeMS"></a>

在 Amazon DocumentDB 中， 會`cursor.maxTimeMS`重設每個`getMore`請求的計數器。因此，如果`maxTimeMS`指定 3000MS，則查詢需要 2800MS，而每個後續`getMore`請求需要 300MS，則游標不會逾時。游標只會在查詢或個別`getMore`請求的單一操作耗用超過指定的 時逾時`maxTimeMS`。此外，檢查游標執行時間的掃描器會以五 (5) 分鐘精細程度執行。

### explain()
<a name="functional-differences.explain"></a>

Amazon DocumentDB 在利用分散式、容錯、自我修復儲存系統的專用資料庫引擎上模擬 MongoDB 3.6、4.0、5.0 和 8.0 APIs。因此，Amazon DocumentDB 和 MongoDB 之間的查詢計劃和 的輸出`explain()`可能不同。想要控制其查詢計劃的客戶可以使用 `$hint` 運算子強制選取偏好的索引。

### 索引組建
<a name="functional-differences.background-indexes"></a>

Amazon DocumentDB 僅允許在任何指定時間在集合上發生一個索引組建。在前景或背景中。當索引建置目前正在進行時，如果在相同的集合上進行如 `createIndex()` 或 `dropIndex()` 等操作，最新嘗試進行的操作將會失敗。

根據預設，Amazon DocumentDB 和 MongoDB 4.0 版中的索引建置會在前景中發生。MongoDB 4.2 版和更新版本指定為 createIndexes 或其 Shell 協助程式`createIndex()`和 時，會忽略背景索引建置選項。 `createIndexes()`

存留時間 (TTL) 索引會在索引建置完成後開始過期文件。

### 路徑中具有空索引鍵的查詢
<a name="functional-differences.lookup-empty"></a>

當您使用包含空字串做為路徑的一部分的索引鍵 （例如 `x.`、`x..b`) 進行查詢，且物件在陣列中具有空字串索引鍵路徑 （例如 `{"x" : [ { "" : 10 }, { "b" : 20 } ]}`) 時，Amazon DocumentDB 會傳回與在 MongoDB 中執行相同查詢不同的結果。

在 MongoDB 中，當空字串索引鍵不在路徑結尾時，陣列中的空索引鍵路徑查詢會如預期般運作。不過，當空字串索引鍵在路徑查詢結尾時，它不會查看陣列。

不過在 Amazon DocumentDB 中，只會讀取陣列中的第一個元素，因為 會將空字串`getArrayIndexFromKeyString`轉換為 `0`，因此字串索引鍵查詢會被視為陣列索引查詢。

### MongoDB APIs、操作和資料類型
<a name="functional-differences.mongo-apis"></a>

Amazon DocumentDB 與 MongoDB 3.6、4.0、5.0 和 8.0 APIs。若要查看最新的支援功能清單，請參閱 [Amazon DocumentDB 中支援的 MongoDB APIs、操作和資料類型](mongo-apis.md)。

### `mongodump` 和 `mongorestore` 公用程式
<a name="functional-differences.mongodump-mongorestore"></a>

Amazon DocumentDB 不支援管理員資料庫，因此在使用 或 `mongorestore`公用程式時不會傾印`mongodump`或還原管理員資料庫。當您使用 在 Amazon DocumentDB 中建立新資料庫時`mongorestore`，除了還原操作之外，還需要重新建立使用者角色。

**注意**  
我們建議使用 MongoDB 資料庫工具，包括適用於 Amazon DocumentDB 的 100.6.1 版。您可以在[此處](https://www.mongodb.com/download-center/database-tools/releases/archive)存取 MongoDB 資料庫工具下載。

### 結果排序
<a name="functional-differences.result-ordering"></a>

Amazon DocumentDB 不保證結果集的隱含結果排序順序。為確保結果集的排序，請使用 `sort()` 明確指定排序順序。

以下範例會根據庫存欄位以遞減順序排序庫存集合中的項目。

```
db.inventory.find().sort({ stock: -1 })
```

使用`$sort`彙總階段時，除非`$sort`階段是彙總管道中的最後一個階段，否則不會保留排序順序。使用`$sort`彙總階段搭配`$group`彙總階段時，`$sort`彙總階段只會套用至 `$last` `$first`和 累積器。在 Amazon DocumentDB 4.0 中，新增了 的支援`$push`，以遵守上一個`$sort`階段的排序順序。

### 可重試的寫入
<a name="functional-differences.retryable-writes"></a>

從 MongoDB 4.2 相容驅動程式開始，預設會啟用可重試寫入。不過，Amazon DocumentDB 目前不支援可重試的寫入。功能差異會在類似下列錯誤訊息中以資訊清單列出。

```
{"ok":0,"errmsg":"Unrecognized field: 'txnNumber'","code":9,"name":"MongoError"} 
```

可透過連線字串 （例如 `MongoClient("mongodb://my.mongodb.cluster/db?retryWrites=false")`) 或 MongoClient 建構函數的關鍵字引數 （例如 ) 停用可重試寫入`MongoClient("mongodb://my.mongodb.cluster/db", retryWrites=False)`。

以下是透過連線字串停用可重試寫入的 Python 範例。

```
client = pymongo.MongoClient('mongodb://<username>:<password>@docdb-2019-03-17-16-49-12.cluster-ccuszbx3pn5e.us-east-1.docdb.amazonaws.com:27017/?replicaSet=rs0',w='majority',j=True,retryWrites=False) 
```

### 稀鬆索引
<a name="functional-differences.sparse-index"></a>

若要使用您在查詢中建立的稀疏索引，您必須在涵蓋索引的欄位上使用 `$exists` 子句。如果您省略 `$exists`，Amazon DocumentDB 將不會使用稀疏索引。

下列是 範例。

```
db.inventory.count({ "stock": { $exists: true }})
```

對於稀疏的多索引鍵索引，如果查詢文件會導致一組值，且僅缺少一部分索引欄位，Amazon DocumentDB 不支援唯一的索引鍵限制條件。例如，若輸入為 `"a" : [ { "b" : 2 }, { "c" : 1 } ]` 則不支援 `createIndex({"a.b" : 1 }, { unique : true, sparse :true })`，因為 `"a.c"` 會儲存在索引中。

### 在`$all`表達式`$elemMatch`內使用
<a name="functional-differences.elemMatch"></a>

Amazon DocumentDB 目前不支援在`$all`表達式中使用`$elemMatch`運算子。您可以如下所示搭配 `$elemMatch` 使用 `$and`，解決問題。

原始操作：

```
db.col.find({
  qty: {
    $all: [
      { "$elemMatch": { part: "xyz", qty: { $lt: 11 } } },
      { "$elemMatch": { num: 40, size: "XL" } }
    ]
  }
})
```

更新的操作：

```
db.col.find({
  $and: [
    { qty: { "$elemMatch": { part: "xyz", qty: { $lt: 11 } } } },
    { qty: { "$elemMatch": { qty: 40, size: "XL" } } }
  ]
})
```

### 欄位名稱中的 Dollar(\$1) 和 dot(.)
<a name="functional-differences-dollardot"></a>

Amazon DocumentDB 不支援在巢狀物件中查詢 \$1in、\$1nin 和 \$1all 中的 Dollar(\$1) 字首欄位。例如，下列查詢在 Amazon DocumentDB 中無效：

```
coll.find({"field": {"$all": [{ "$a": 1 }]}})
```

### `$lookup`
<a name="functional-differences.lookup"></a>

Amazon DocumentDB 支援執行相等比對的能力 （例如，左外部聯結），也支援不相關的子查詢，但不支援關聯的子查詢。

#### 使用索引搭配 `$lookup`
<a name="functional-differences.lookup-index"></a>

您現在可以將索引與`$lookup`階段運算子搭配使用。根據您的使用案例，您可以使用多種索引演算法來最佳化效能。本節將說明 的不同索引演算法，`$lookup`並協助您選擇最適合工作負載的索引演算法。

根據預設，Amazon DocumentDB 會在使用 `allowDiskUse:false` 時使用雜湊演算法，並在`allowDiskUse:true`使用 時排序合併。

**注意**  
`find` 命令目前不支援 `allowDiskUse`選項。僅支援 選項做為彙總的一部分。我們建議您搭配 使用彙總架構`allowDiskUse:true`，來處理可能超過記憶體限制的大型查詢。

對於某些使用案例，可能需要強制查詢最佳化工具使用不同的演算法。以下是`$lookup`彙總運算子可以使用的不同索引演算法：
+ **巢狀迴圈**：如果外部集合 <1 GB，且外部集合中的 欄位具有索引，巢狀迴圈計劃通常有利於工作負載。如果使用巢狀迴圈演算法，解釋計畫將顯示階段為 `NESTED_LOOP_LOOKUP`。
+ **排序合併**：如果外部集合在查詢中使用的欄位上沒有索引，且工作資料集不適合記憶體，則排序合併計畫通常有利於工作負載。如果正在使用排序合併演算法，解釋計畫會將階段顯示為 `SORT_LOOKUP`。
+ **雜湊**：如果外部集合 < 1 GB，且工作資料集適合記憶體，雜湊計劃通常有利於工作負載。如果使用雜湊演算法，解釋計畫會將階段顯示為 `HASH_LOOKUP`。

您可以在`explain`查詢上使用 來識別用於`$lookup`運算子的索引演算法。以下是範例：

```
db.localCollection.explain().aggregate(
   [ 
      { 
         $lookup: 
            { 
               from: "foreignCollection", 
               localField: "a", 
               foreignField: "b", 
               as: "joined" 
            } 
      } 
   ]
)

output
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.localCollection",
        "winningPlan" : {
            "stage" : "SUBSCAN",
            "inputStage" : {
                "stage" : "SORT_AGGREGATE",
                "inputStage" : {
                    "stage" : "SORT",
                    "inputStage" : {
                        "stage" : "NESTED_LOOP_LOOKUP",
                        "inputStages" : [
                            {
                                "stage" : "COLLSCAN"
                            },
                            {
                                "stage" : "FETCH",
                                "inputStage" : {
                                    "stage" : "COLLSCAN"
                                }
                            }
                        ]
                    }
                }
            }
        }
    },
    "serverInfo" : {
        "host" : "devbox-test",
        "port" : 27317,
        "version" : "3.6.0"
    },
    "ok" : 1
}
```

除了使用 `explain()`方法之外，您也可以使用分析器來檢閱與您使用 `$lookup`運算子搭配使用的演算法。如需分析器的詳細資訊，請參閱 [分析 Amazon DocumentDB 操作](profiling.md)。

#### 使用 `planHint`
<a name="functional-differences.lookup-plan"></a>

如果您想要強制查詢最佳化工具搭配 使用不同的索引演算法`$lookup`，您可以使用 `planHint`。若要這樣做，請使用彙總階段選項中的註解來強制不同的計劃。以下是註解的語法範例：

```
comment : {
    comment :  "<string>",
    lookupStage : { planHint : "SORT" | "HASH" | "NESTED_LOOP" }
}
```

以下是使用 `planHint`強制查詢最佳化工具使用`HASH`索引演算法的範例：

```
db.foo.aggregate(
   [                           
      {   
         $lookup:
            {   
               from: "foo",
               localField: "_id",
               foreignField: "_id",
               as: "joined"
            },
      }
   ]
),
{
   comment : "{ \"lookupStage\" : { \"planHint\": \"HASH\" }}"
```

若要測試哪種演算法最適合您的工作負載，您可以使用 `explain`方法的 `executionStats` 參數來測量`$lookup`階段的執行時間，同時修改索引演算法 （即 `HASH`/`SORT`/`NESTED_LOOP`)。

下列範例示範如何使用 `executionStats` 來測量使用`SORT`演算法的`$lookup`階段執行時間。

```
db.foo.explain("executionStats").aggregate(
   [    
      {   
         $lookup:
            {
               from: "foo",
               localField: "_id",
               foreignField: "_id",
               as: "joined"
            },
      }
   ]
),
{
   comment : "{ \"lookupStage\" : { \"planHint\": \"SORT\" }}"
```

### `$natural` 和反向排序
<a name="functional-differences.natural"></a>

Amazon DocumentDB 僅支援`$natural`轉送集合掃描。反向收集掃描 (`{$natural: -1}`) 將導致 `MongoServerError`。