Mongo DB 3.4.6版
我有一个文档结构类似于以下的集合:
{
organization: "ABC123",
tags: ["MARTHA WASHINGTON", "+15552082000"],
updatedAt : ISODate("2020-10-09T17:19:44.861Z"),
createdAt : ISODate("2020-01-14T19:46:15.957Z"),
}
我需要能够按组织进行查询;以";在标签数组上,并可选择按updatedAt或createdAt排序。为了实现这一点,我创建了以下索引:
{
"organization" : 1,
"tags" : 1,
"createdAt" : -1
}
这是一个多键复合索引,基于我对Mongo的理解,它应该允许我在所有情况下覆盖查询。如果我执行这样的查询:
db.getCollection('data').find({"organization": "ABC123", "search": /^MARTHA WASHINGTO/})
查询由索引覆盖——我看到一个FETCH/IXSCAN阶段。
同样,如果我删除regex查询并添加排序,那么查询就完全覆盖了。
db.getCollection('data').find({"organization": "ABC123", "search": "MARTHA WASHINGTON"}).sort({"createdAt":-1})
但是,如果我组合正则表达式和排序选项,我会突然在查询中看到一个额外的sort阶段。示例查询:
db.getCollection('data').find({"organization": "ABC123", "search": /^MARTHA WASHINGTO/}).sort({"createdAt":-1})
以下是解释的获胜计划输出:
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"createdAt" : -1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"organization" : 1,
"tags" : 1,
"createdAt" : -1
},
"indexName" : "tag matches by organization",
"isMultiKey" : true,
"multiKeyPaths" : {
"organization" : [],
"search" : [
"search"
],
"createdAt" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"organization" : [
"["ABC123", "ABC123"]"
],
"tags" : [
"["MARTHA WASHINGTON", "MARTHA WASHINGTOO")",
"[/^MARTHA WASHINGTON/, /^MARTHA WASHINGTON/]"
],
"createdAt" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
我很困惑为什么索引中没有包含这种查询组合。我的理解是,一开始的额外排序阶段会导致大型集合的性能缓慢。有人能提供一些指导吗?我错过了什么限制吗?
更新:删除正则表达式查询时的获胜计划
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"organization" : 1,
"search" : 1,
"createdAt" : -1
},
"indexName" : "tag matches by organization",
"isMultiKey" : true,
"multiKeyPaths" : {
"organization" : [],
"search" : [
"search"
],
"createdAt" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"organization" : [
"["ABC123", "ABC123"]"
],
"tags" : [
"["MARTHA WASHINGTON", "MARTHA WASHINGTON"]"
],
"createdAt" : [
"[MaxKey, MinKey]"
]
}
}
},
另一个答案不太准确。从文档
对于区分大小写的正则表达式查询,如果字段存在索引,则MongoDB将正则表达式与索引中的值进行匹配,这可能比集合扫描更快。
Mongo能够利用带有正则表达式的索引,显然,如果您的正则表达式是后缀正则表达式,那么集合扫描实际上可能会更快,因为Mongo必须读取整个索引树才能满足它。
那么,您的查询中发生了什么?为什么获胜计划是sort
?虽然这可能是获取结果的最佳方式,但Mongo也有可能只是选择了错误的计划。
首先让我们了解一下Mongo是如何选择获胜计划的,计划评估是基于对给定查询的候选计划进行比较,看看哪一个返回的第一批结果(默认为101个文档(具有最少的总体";工作";。works分数是查询阶段(索引键比较、获取文档等(所涉及的不同工作的代理。如果多个计划在评估期间执行相同的工作,那么有一些小的平局奖金可以帮助选择要缓存的计划。基本上Mongo执行一个小的";种族;等待谁获胜。
因此,在您的情况下,由于带有索引的regex特性,sort
阶段获胜,如果您完全运行计划而不是小样本,则可能会选择不同的计划。
我建议您使用提示进行自己的测试,这会强制Mongo使用某个索引,这意味着您可以强制Mongo为您的查询制定获胜计划。我个人认为(显然具体取决于正则表达式(,您可以通过这样做来提高性能,因为排序第一几乎不是所有的"正则表达式";最好的";计划
假设集合中有两个字段:name和age,并按名称进行筛选,按年龄排序。
假设您有以下文档:
JON 30
JON 45
JONATHAN 40
假设您在(name,age(上创建了一个索引。此索引按上面列出的顺序排列文档。
如果查询name = JON
并按年龄排序,则所有条件都与索引完全匹配,并且只能通过索引遍历获得(JON, 30), (JON, 45)
的输出。
如果查询name =~ ^JON
并按年龄排序,那么您期望的输出现在是(JON, 30), (JONATHAN, 40), (JON, 45)
。这种排序在索引中不存在,因为名称匹配现在不准确,因此服务器必须对结果集进行排序才能提供它