Querying

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

查询文档

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

要查询文档,请使用 find() 操作。find() 命令具有单个文档参数,该参数定义了在选择要返回的文档时要使用的标准。find() 的输出是一个文档,其格式为一行文本,不含换行符。要设置输出文档的格式以方便阅读,请使用 find().pretty()。 本主题中的所有示例都使用 .pretty() 设置输出的格式。

以下代码示例使用您在前面两个练习 example — 和 insertOne() 中插入的四个文档,这两个练习位于 insertMany() 的“添加文档”部分。

检索集合中的所有文档

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

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

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

检索与字段值匹配的文档

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

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

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

检索与嵌入文档匹配的文档

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

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

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

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

检索与嵌入文档中的字段值匹配的文档

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

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

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

检索与数组匹配的文档

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

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

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

检索与数组中的值匹配的文档

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

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

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

使用运算符检索文档

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

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

有关支持的查询运算符的列表,请参阅 查询和投影运算符

查询计划

如何查看查询计划的 executionStats

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

注意

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

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

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

以下是操作示例。

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

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

  1. {
  2. "queryPlanner" : {
  3. "plannerVersion" : 1,
  4. "namespace" : "test.fish",
  5. "winningPlan" : {
  6. "stage" : "SUBSCAN",
  7. "inputStage" : {
  8. "stage" : "LIMIT_SKIP",
  9. "inputStage" : {
  10. "stage" : "COLLSCAN"
  11. }
  12. }
  13. }
  14. },
  15. "executionStats" : {
  16. "executionSuccess" : true,
  17. "executionTimeMillis" : "0.063",
  18. "planningTimeMillis" : "0.040",
  19. "executionStages" : {
  20. "stage" : "SUBSCAN",
  21. "nReturned" : "2",
  22. "executionTimeMillisEstimate" : "0.012",
  23. "inputStage" : {
  24. "stage" : "LIMIT_SKIP",
  25. "nReturned" : "2",
  26. "executionTimeMillisEstimate" : "0.005",
  27. "inputStage" : {
  28. "stage" : "COLLSCAN",
  29. "nReturned" : "2",
  30. "executionTimeMillisEstimate" : "0.005"
  31. }
  32. }
  33. }
  34. },
  35. "serverInfo" : {
  36. "host" : "enginedemo",
  37. "port" : 27017,
  38. "version" : "3.6.0"
  39. },
  40. "ok" : 1
  41. }

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

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

查询计划缓存

为了优化性能和缩短规划时间,Amazon DocumentDB 在内部缓存查询计划。这使具有相同形状的查询能够使用缓存的计划直接执行。

但是,此缓存有时可能会导致同一查询出现随机延迟;例如,运行通常需要一秒的查询偶尔可能需要十秒。这是因为随着时间推移,读取器实例缓存了查询的各种形状,从而消耗内存。如果您遇到这种随机缓慢,则无需执行任何操作来释放内存 - 系统将管理内存使用率,一旦内存达到特定阈值,系统就会自动释放内存。

Explain 结果

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

  1. {
  2. "queryPlanner" : {
  3. "plannerVersion" : <int>,
  4. "namespace" : <string>,
  5. "winningPlan" : {
  6. "stage" : <STAGE1>,
  7. ...
  8. "inputStage" : {
  9. "stage" : <STAGE2>,
  10. ...
  11. "inputStage" : {
  12. ...
  13. }
  14. }
  15. }
  16. }
  17. }

以下部分将定义常见的 explain 结果。

扫描和筛选阶段

优化程序可能会选择下列扫描之一:

列式

此阶段是顺序集合扫描。

  1. {
  2. "stage" : "COLLSCAN"
  3. }

IASCAN

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

  1. db.foo.find({"a": 1})
  2. {
  3. "stage" : "IXSCAN",
  4. "direction" : "forward",
  5. "indexName" : <idx_name>
  6. }

FETCH

如果优化程序在 IXSCAN 以外的阶段检索到文档,则结果将包含 FETCH 阶段。例如,上面的 IXSCAN 查询可能会生成 FETCH 和 IXSCAN 阶段的组合:

  1. db.foo.find({"a": 1})
  2. {
  3. "stage" : "FETCH",
  4. "inputStage" : {
  5. "stage" : "IXSCAN",
  6. "indexName" : <idx_name>
  7. }
  8. }

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

索引交集

随机排序

Amazon DocumentDB 可能包含 IXAND 阶段,该阶段具有 inputStages XISCAN 数组 (如果它可以使用索引交集)。例如,我们可以看到类似以下内容的输出:

  1. {
  2. "stage" : "FETCH",
  3. "inputStage" : {
  4. "stage" : "IXAND",
  5. "inputStages" : [
  6. {
  7. "stage" : "IXSCAN",
  8. "indexName" : "a_1"
  9. },
  10. {
  11. "stage" : "IXSCAN",
  12. "indexName" : "b_1"
  13. }
  14. ]
  15. }
  16. }

索引并集

单数

与索引交集类似,Amazon DocumentDB 可能包括 IXOR 阶段,以及 inputStages 运算符的 $or 数组。

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

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

  1. {
  2. "stage" : "FETCH",
  3. "inputStage" : {
  4. "stage" : "IXOR",
  5. "inputStages" : [
  6. {
  7. "stage" : "IXSCAN",
  8. "indexName" : "a_1"
  9. },
  10. {
  11. "stage" : "IXSCAN",
  12. "indexName" : "b_1"
  13. }
  14. ]
  15. }
  16. }

多索引交集/并集

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

  1. {
  2. "stage" : "FETCH",
  3. "inputStage" : {
  4. "stage" : "IXOR",
  5. "inputStages" : [
  6. {
  7. "stage" : "IXSCAN",
  8. ...
  9. },
  10. {
  11. "stage" : "IXAND",
  12. "inputStages" : [
  13. {
  14. "stage" : "IXSCAN",
  15. ...
  16. },
  17. {
  18. "stage" : "IXSCAN",
  19. ...
  20. }
  21. ]
  22. }
  23. ]
  24. }
  25. }

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

复合索引

Amazon DocumentDB 复合索引的使用不受索引字段的开始子集限制;它可以将索引与后缀部分一起使用,但可能效率较差。

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

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

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

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

排序阶段

如果请求的排序键上有索引,则 DocumentDB 可以使用索引获取顺序。在这种情况下,结果将不包含 SORT 阶段,而是包含 IXSCAN 阶段。如果优化程序倾向于普通排序,它将包含如下所示的阶段:

  1. {
  2. "stage" : "SORT",
  3. "sortPattern" : {
  4. "a" : 1,
  5. "b" : -1
  6. }
  7. }

组阶段

Amazon DocumentDB 支持两种不同的组策略:

  • SORT_AGGREGATE: On disk sort aggregate.
  • HASH_AGGREGATE: In memory hash aggregate.