

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

# 功能差异：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 语句（ `findAndModify`、`update`、`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>

在 3.6 版本中，Amazon DocumentDB 与 MongoDB 之间存在带 `$project` 运算符的功能差异，该差异已在 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)
+ [M APIs ongoDB、操作和数据类型](#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)
+ [字段名称中的美元符号（\$1）和句点（.）](#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`因此，如果指定了 3000MS `maxTimeMS`，则该查询耗时 2800MS，而每个后续`getMore`请求耗时 300MS，则游标不会超时。游标仅在单个操作（无论是查询还是单个`getMore`请求）耗时超过指定值时才将超时`maxTimeMS`。此外，检查游标执行时间的扫描器以五 (5) 分钟间隔尺寸运行。

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

Amazon DocumentDB 在专门构建的数据库引擎 APIs 上模拟 MongoDB 3.6、4.0、5.0 和 8.0，该引擎利用分布式、容错、自我修复的存储系统。因此，查询计划和 `explain()` 的输出在 Amazon DocumentDB 和 MongoDB 之间可能有所不同。希望控制其查询计划的客户可以使用 `$hint` 运算符强制选择首选索引。

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

在任何给定时间，Amazon DocumentDB 只允许在一个集合中构建一个索引。或在前台，或在后台。如果当前正在构建索引，在同一集合上发生了 `createIndex()` 或 `dropIndex()` 之类的操作，则新尝试的操作将失败。

默认情况下，Amazon DocumentDB 和 MongoDB 版本 4.0 中的索引构建在前台进行。如果指定为 createIndexes 或其 Shell 助手 `createIndex()` 和 `createIndexes()`，则 MongoDB 版本 4.2 及更高版本将忽略后台索引构建选项。

在构建索引完成后，有效时间 (TTL) 索引开始使文档过期。

### 在路径中用空键查找
<a name="functional-differences.lookup-empty"></a>

当您用包含空字符串作为路径一部分的键（例如`x.`、`x..b`）查找，并且该对象在数组内部有一个空字符串键路径（例如`{"x" : [ { "" : 10 }, { "b" : 20 } ]}`）时，Amazon DocumentDB 将返回与您在 MongoDB 中运行相同查找时不同的结果。

在 MongoDB 中，当空字符串键不在路径查找的末尾时，在数组内部查找空键路径如预期那样工作。但是，当空字符串键位于路径查找的末尾时，它不查看数组。

但是，在 Amazon DocumentDB 中，仅读取数组内部的第一个元素，因为 `getArrayIndexFromKeyString` 将空字符串转换成 `0`，从而字符串键查找作为数组索引查找处理。

### M APIs ongoDB、操作和数据类型
<a name="functional-differences.mongo-apis"></a>

亚马逊 DocumentDB 与 MongoDB 3.6、4.0、5.0 和 8.0 兼容。 APIs有关支持的功能 up-to-date列表，请参阅[在 Amazon Document APIs DB 中支持 MongoDB、操作和数据类型](mongo-apis.md)。

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

Amazon DocumentDB 不支持管理数据库，因此在使用 `mongodump` 或 `mongorestore` 实用程序时不转储或还原管理数据库。当您在 Amazon DocumentDB 中使用 `mongorestore`创建新的数据库时，除了执行还原操作外，还需要重新创建用户角色。

**注意**  
我们对 Amazon DocumentDB 推荐高达且包括版本 100.6.1 的 MongoDB 数据库工具。您可以在[此处](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` 聚合阶段仅适用于 `$first` 和 `$last` 累加器。在 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` 运算符。一种解决方法是，您可以将 `$and` 运算符与 `$elemMatch` 结合使用，如下所示。

原始运算：

```
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" } } }
  ]
})
```

### 字段名称中的美元符号（\$1）和句点（.）
<a name="functional-differences-dollardot"></a>

Amazon DocumentDB 不支持在嵌套对象中查询 \$1in、\$1nin 和 \$1all 中以美元符号（\$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`。
+ **哈希**：如果外部集合小于 1GB 且工作数据集适合内存，则哈希计划通常有益于工作负载。如果正在使用哈希算法，则解释计划将该阶段显示为`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`）。

以下示例说明使用 `SORT` 算法，如何使用 `executionStats` 计量 `$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`。