在 Azure Cosmos DB for MongoDB API 中使用没有默认索引策略的 sort() 游标方法

Mat*_*ias 9 azure-cosmosdb azure-cosmosdb-mongoapi

使用 Cosmos DB for MongoDB API(版本 3.4),以下查找查询与方法游标排序的组合似乎行为不正确:

db.test.find({"field1": "value1"}).sort({"field2": 1})
Run Code Online (Sandbox Code Playgroud)

如果满足以下所有条件,则会发生错误:

  • 默认索引策略被丢弃 - 无论之后是否使用 createIndex() 创建了自定义索引。
  • find() 查询不返回任何文档(Find(filter).Count() == 0)
  • 定义排序顺序的排序文档仅包含一个字段。这个字段是否存在或已被索引并不重要。在排序文档中使用两个字段会返回 0 个命中,这是正确的行为。

如果满足以下所有条件,也会发生错误:

  • 默认索引策略被丢弃
  • find() 查询返回一个或多个文档
  • Sort 文档只包含一个字段。此字段尚未编入索引。

错误信息:

排除指定 order-by 项对应的索引路径。

该故障仅在使用 CosmosDB 时发生,使用本机 MongoDB (mongoDB Atlas, v4.0) 时其行为正确。

使用具有 MongoDB 3.4 线路协议(预览功能)的 Azure Cosmos DB for MongoDB API。MongoDB C#/.NET 驱动程序和 mongo shell 都会出现此问题。

此外,该问题仅出现在 find() 中。包含 $match 和 $sort 的等效聚合管道行为正确。

再生产

  1. 使用“Azure Cosmos DB for MongoDB API”创建 Azure Cosmos DB 帐户。启用 MongoDB 3.4 预览功能(3.2 版尚未测试)。
  2. 创建一个新的数据库
  3. 创建一个新的集合,定义一个分片键
  4. 删除默认索引策略(使用 db.test.dropIndexes() )
  5. (可选)创建新的自定义索引
  6. (可选)插入文档

在 mongo shell 中执行命令(或使用 mongoDB C#/.NET 驱动程序的等效代码):

    db.test.find({"field1": "value1"}).sort({"field2": 1})
Run Code Online (Sandbox Code Playgroud)

预期结果

符合查询条件的所有文档。如果没有,则不应退回任何文件。

实际结果

error: error: { "_t" : "OKMongoResponse", "ok" : 0, "code" : 2, "errmsg" : "Message: {\"Errors\":[\"指定顺序对应的索引路径-by 项目被排除。\"]}\r\nActivityId:c50cc751-0000-0000-0000-000000000000,请求 URI:/apps/[...]/,RequestStats:\r\nRequestStartTime:2019-07-11T08 :58:48.9880813Z, RequestEndTime: 2019-07-11T08:58:49.0081101Z, 尝试的区域数: 1\r\nResponseTime: 2019-07-11T08:58:49.0081101Z, StoreResult: ...]/, LSN: 359549, GlobalCommittedLsn: 359548, PartitionKeyRangeId: 0, IsValid: True, StatusCode: 400, SubStatusCode: 0, RequestCharge: 1, ItemLSN: -1, SessionToken: -1#359549, UsingLocalLSN: True传输异常:null,资源类型:文档,操作类型:查询\r\n,SDK:Microsoft.Azure.Documents.Common/2.4.0.0",[...]

解决方法

向排序文档添加额外的“虚拟”字段可防止出现错误:

db.test.find({"field1": "value1"}).sort({"field2": 1, "dummyfield": 1}).count()
Run Code Online (Sandbox Code Playgroud)

解决方法并不令人满意。它可能会伪造结果。

我做错了什么,还是 Cosmos DB 在这里的行为有缺陷?

Mat*_*ias 5

根据 Microsoft 支持,需要在被排序的字段上创建索引。可以删除默认索引并创建自定义索引。至于每次添加新字段时不修改索引的问题,除了执行客户端排序之外别无他法。不幸的是,客户端排序会在客户端占用大量 CPU 内存,并且当您获得更多要索引的字段时,索引排序将需要工作。

因此我没有找到一个真正令人满意的解决方案:

  • 使用默认索引策略。但是,这可能会导致一个巨大的索引。
  • 索引所有需要排序的元素。每次必须索引新元素时,这都会导致手动修改索引策略。
  • 仅使用客户端排序。在我看来,这会导致 MongoDB 功能受到严重限制。
  • 使用聚合框架而不是 find 方法。这会导致复杂性和流量增加。
  • 迁移到本机 MongoDB。