

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

# Amazon DocumentDB 中的查询
<a name="querying"></a>

本节介绍使用 Amazon DocumentDB 进行查询的所有方面。

**Topics**
+ [查询文档](#querying.docs)
+ [查询计划](#querying.queryplan)
+ [解释结果](#querying.explainresults)
+ [查询计划器 v2](query-planner.md)
+ [查询计划器 v3](query-planner-v3.md)
+ [地理空间数据](geospatial.md)
+ [部分索引](partial-index.md)
+ [文本搜索](text-search.md)

## 查询文档
<a name="querying.docs"></a>

有时，您可能需要查看在线商店的库存，这样客户就能看到并购买您销售的物品。查询集合相对容易，无论您想要集合中的所有文档，还是仅需要那些满足特定标准的文档。

要查询文档，请使用 `find()` 操作。`find()` 命令具有单个文档参数，该参数定义了在选择要返回的文档时要使用的标准。`find()` 的输出是一个文档，其格式为一行文本，不含换行符。要格式化输出文档，从而更加轻松地读取，请使用 `find().pretty()`。本主题中的所有示例都使用 `.pretty()` 设置输出的格式。

下面的代码示例使用在前面两个练习中插入到 `example` 集合中的四个文档：`insertOne()` 和 `insertMany()`，它们位于[使用文档](https://docs.amazonaws.cn//documentdb/latest/developerguide/document-database-working-with-documents.html)的添加文档部分。

**Topics**
+ [检索所有文档](#querying.alldocs)
+ [匹配字段值](#querying.matchfield)
+ [嵌入文档](#querying.embedded-doc)
+ [嵌入文档中的字段值](#querying.embedded-docs-field)
+ [匹配数组](#querying.array)
+ [匹配数组中的值](#querying.array-values)
+ [使用运算符](#querying.operators)

### 检索集合中的所有文档
<a name="querying.alldocs"></a>

要检索集合中的所有文档，请将 `find()` 操作和空查询文档结合使用。

以下查询返回 `example` 集合中的所有文档。

```
db.example.find( {} ).pretty()
```

### 检索与字段值匹配的文档
<a name="querying.matchfield"></a>

要检索与字段和值匹配的所有文档，请将 `find()` 操作和查询文档（标识要匹配的字段和值）结合使用。

通过使用前述文档，此查询将返回其中“Item”字段等于“Pen”的所有文档。

```
db.example.find( { "Item": "Pen" } ).pretty()
```

### 检索与嵌入文档匹配的文档
<a name="querying.embedded-doc"></a>

要查找与嵌入文档匹配的所有文档，请将 `find()` 操作和查询文档（指定嵌入文档名称和嵌入文档的所有字段和值）结合使用。

在与嵌入文档匹配时，该文档的嵌入文档的名称必须与查询中的名称相同。此外，嵌入文档中的字段和值必须与查询匹配。

以下查询仅返回“Poster Paint”文档。这是因为“Pen”具有不同的“`OnHand`”和“`MinOnHand`”值，并且“Spray Paint”比查询文档多一个字段 (`OrderQnty`)。

```
db.example.find({"Inventory": {
    "OnHand": 47,
    "MinOnHand": 50 } } ).pretty()
```

### 检索与嵌入文档中的字段值匹配的文档
<a name="querying.embedded-docs-field"></a>

要查找与嵌入文档匹配的所有文档，请将 `find()` 操作和查询文档（指定嵌入文档名称和嵌入文档的所有字段和值）结合使用。

考虑到上述文档，以下查询使用“点表示法”来指定嵌入文档和感兴趣的字段。将返回所有与这些内容匹配的文档，而不管嵌入文档中可能存在哪些其他字段。此查询将返回“Poster Paint”和“Spray Paint”，因为它们与指定的字段和值匹配。

```
db.example.find({"Inventory.OnHand": 47, "Inventory.MinOnHand": 50 }).pretty()
```

### 检索与数组匹配的文档
<a name="querying.array"></a>

要查找所有与数组匹配的文档，请将 `find()` 操作和您感兴趣的数组名称以及数组中的所有值结合使用。此查询将返回所有包含带该名称的数组（其中数组值和顺序与查询中的完全相同）的文档。

以下查询仅返回“Pen”，因为“Poster Paint”具有其他颜色 (White)，并且“Spray Paint”具有顺序不同的颜色。

```
db.example.find( { "Colors": ["Red","Green","Blue","Black"] } ).pretty() 
```

### 检索与数组中的值匹配的文档
<a name="querying.array-values"></a>

要查找所有具有特定数组值的文档，请将 `find()` 操作与您感兴趣的数组名称和值结合使用。

```
db.example.find( { "Colors": "Red" } ).pretty() 
```

上述操作将返回所有三个文档，因为它们都有一个名为 `Colors` 的数组，并且此数组中的某个位置具有“`Red`”值。如果您指定值“`White`”，则查询将仅返回“Poster Paint”。

### 使用运算符检索文档
<a name="querying.operators"></a>

以下查询返回“`Inventory.OnHand`”值小于 50 的所有文档。

```
db.example.find(
        { "Inventory.OnHand": { $lt: 50 } } )
```

有关支持的查询运算符的列表，请参阅 [查询和投影运算符](mongo-apis.md#mongo-apis-query)。

## 查询计划
<a name="querying.queryplan"></a>

### 如何查看查询计划的 `executionStats`？
<a name="querying.queryplan-executionStats"></a>

在确定查询的执行速度低于预期速度的原因时，了解查询计划的 `executionStats` 会很有用。`executionStats` 提供从特定阶段返回的文档数量 (`nReturned`)、在每个阶段花费的执行时间 (`executionTimeMillisEstimate`) 以及生成查询计划所需的时间长度 (`planningTimeMillis`)。您可以确定查询中最耗时的阶段，以帮助您根据 `executionStats` 的输出集中精力完成优化工作，如以下查询示例所示。`executionStats` 参数当前不支持 `update` 和 `delete` 命令。

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

在 `explain()` 命令下运行要改进的查询，如下所示。

```
db.runCommand({explain: {query document}}).
explain("executionStats").executionStats;
```

以下是操作示例。

```
db.fish.find({}).limit(2).explain("executionStats");
```

此操作的输出将类似于下文。

```
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.fish",
        "winningPlan" : {
            "stage" : "SUBSCAN",
            "inputStage" : {
                "stage" : "LIMIT_SKIP",
                "inputStage" : {
                    "stage" : "COLLSCAN"
                }
            }
        }
    },
    "executionStats" : {
        "executionSuccess" : true,
        "executionTimeMillis" : "0.063",
        "planningTimeMillis" : "0.040",
        "executionStages" : {
            "stage" : "SUBSCAN",
            "nReturned" : "2",
            "executionTimeMillisEstimate" : "0.012",
            "inputStage" : {
                "stage" : "LIMIT_SKIP",
                "nReturned" : "2",
                "executionTimeMillisEstimate" : "0.005",
                "inputStage" : {
                    "stage" : "COLLSCAN",
                    "nReturned" : "2",
                    "executionTimeMillisEstimate" : "0.005"
                }
            }
        }
    },
    "serverInfo" : {
        "host" : "enginedemo",
        "port" : 27017,
        "version" : "3.6.0"
    },
    "ok" : 1
}
```

如果您只想看到上面查询的 `executionStats`，您可以使用以下命令。对于较小的集合，如果性能增益微乎其微，Amazon DocumentDB 查询处理器可以选择不使用索引。

```
db.fish.find({}).limit(2).explain("executionStats").executionStats;
```

### 查询计划缓存
<a name="querying.queryplan-cached"></a>

为了优化性能并缩短计划持续时间，Amazon DocumentDB 在内部缓存查询计划。这样，具有相同形状的查询就可以使用缓存计划直接执行。

但是，此缓存有时可能会导致同一查询的随机延迟;例如，通常需要 1 秒才能运行的查询有时可能需要 10 秒。这是因为随着时间的推移，读取器实例会缓存各种形状的查询，从而消耗内存。如果您遇到这种随机缓慢的情况，则无需执行任何操作即可释放内存：系统将为您管理内存使用量，一旦内存达到特定阈值，它将自动释放。

## 解释结果
<a name="querying.explainresults"></a>

如果要返回有关查询计划的信息，Amazon DocumentDB 支持详细程度模式 `queryPlanner`。`explain` 结果以类似于以下内容的格式返回优化程序选择的选定查询计划：

```
{
   "queryPlanner" : {
      "plannerVersion" : <int>,
      "namespace" : <string>,
      "winningPlan" : {
         "stage" : <STAGE1>,
         ...
         "inputStage" : {
            "stage" : <STAGE2>,
            ...
            "inputStage" : {
               ...
            }
         }
      }
   }
}
```

以下各节将定义常见的 `explain` 结果。

**Topics**
+ [扫描和过滤阶段](#querying.explainresults-scan-filter)
+ [索引交集](#querying.explainresults-index-intersection)
+ [索引并集](#querying.explainresults-index-union)
+ [多索引交集/并集](#querying.explainresults-multiple-index-union)
+ [复合索引](#querying.explainresults-compound-index)
+ [排序阶段](#querying.explainresults-sort)
+ [小组阶段](#querying.explainresults-group)

### 扫描和过滤阶段
<a name="querying.explainresults-scan-filter"></a>

优化器可以选择以下扫描之一：

COLLSCAN

此阶段是顺序收集扫描。

```
{
    "stage" : "COLLSCAN"
}
```

IXSCAN

此阶段扫描索引键。优化程序可能会在此阶段内检索文档，这可能会导致稍后附加 FETCH 阶段。

```
db.foo.find({"a": 1})
{
    "stage" : "IXSCAN",
    "direction" : "forward",
    "indexName" : <idx_name>
}
```

FETCH

如果优化程序在 IXSCAN 以外的阶段检索文档，则结果将包括 FETCH 阶段。例如，上面的 IXSCAN 查询可能会导致 FETCH 和 IXSCAN 阶段的组合：

```
db.foo.find({"a": 1})
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXSCAN",
        "indexName" : <idx_name>
    }
}
```

IXONLYSCAN 仅扫描索引键。创建复合索引不会避免 FETCH。

### 索引交集
<a name="querying.explainresults-index-intersection"></a>

IXAND

如果 Amazon DocumentDB 可以利用索引交集，则可以包含具有 IXSCAN 的 inputStages 数组的 IXAND 阶段。例如，我们可能会看到如下输出：

```
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXAND",
        "inputStages" : [
            {
                "stage" : "IXSCAN",
                "indexName" : "a_1"
            },
            {
                "stage" : "IXSCAN",
                "indexName" : "b_1"
            }
        ]
    }
}
```

### 索引并集
<a name="querying.explainresults-index-union"></a>

IXOR

与索引交集类似，Amazon DocumentDB 可以包含 `IXOR` 阶段和 `$or` 运算符的 `inputStages` 数组。

```
db.foo.find({"$or": [{"a": {"$gt": 2}}, {"b": {"$lt": 2}}]})
```

对于上述查询，解释输出可能如下所示：

```
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXOR",
        "inputStages" : [
            {
                "stage" : "IXSCAN",
                "indexName" : "a_1"
            },
            {
                "stage" : "IXSCAN",
                "indexName" : "b_1"
            }
        ]
    }
}
```

### 多索引交集/并集
<a name="querying.explainresults-multiple-index-union"></a>

Amazon DocumentDB 可以将多个索引交集或并集阶段组合在一起，然后获取结果。例如：

```
{
    "stage" : "FETCH",
    "inputStage" : {
        "stage" : "IXOR",
        "inputStages" : [
            {
                "stage" : "IXSCAN",
                ...
            },
            {
                "stage" : "IXAND",
                "inputStages" : [
                    {
                        "stage" : "IXSCAN",
                        ...
                    },
                    {
                        "stage" : "IXSCAN",
                        ...
                    }
                ]
            }
        ]
    }
}
```

索引交集或并集阶段的使用不受索引类型（稀疏、复合等）的影响。

### 复合索引
<a name="querying.explainresults-compound-index"></a>

Amazon DocumentDB 复合索引的使用不受索引字段的起始子集的限制；它可以将索引与后缀部分一起使用，但可能不是很有效。

例如，`{ a: 1, b: -1 }` 的复合索引可以支持以下所有三个查询：

`db.orders.find( { a: 1 } )`

`db.orders.find( { b: 1 } )`

`db.orders.find( { a: 1, b: 1 } )`

### 排序阶段
<a name="querying.explainresults-sort"></a>

如果请求的排序键上有索引，则 Amazon DocumentDB 可以使用该索引来获取顺序。在这种情况下，结果将不包括 `SORT` 阶段，而是包括 `IXSCAN` 阶段。如果优化程序偏向于普通排序，它将包括一个类似这样的阶段：

```
{
    "stage" : "SORT",
    "sortPattern" : {
        "a" : 1,
        "b" : -1
    }
}
```

### 小组阶段
<a name="querying.explainresults-group"></a>

Amazon DocumentDB 支持两种不同的组策略：
+ `SORT_AGGREGATE`：在磁盘上对聚合进行排序。
+ `HASH_AGGREGATE`: 在内存中对聚合进行哈希。