我有一个使用Mongoose的普通索引模型。
const mod = new mongoose.Schema({
number: { type: String, required: true, index: { unique: true } },
});
我在查询中使用正则表达式来获取与特定数字相对应的mod。我的正则表达式查询是否会使用此模型上的索引?
query.number = {
$regex: `.*Q10.*`
}
modelName.find(query)
我担心这是在不使用索引的情况下查看整个集合。什么是知道我是否使用索引的最佳方式。或者,如果你碰巧知道一种利用索引的方法,你能告诉我吗?在这里,我正在寻找接近Q10
的所有人,而不是试图获得完全匹配。使用/^Q10.*
会更好并使用索引吗?
引用MongoDB regex information on index以及对此帖stackoverflow previous question
所做的评论答案 0 :(得分:3)
确认给定查询的索引使用情况的最佳方法是使用MongoDB的查询explain()
功能。有关输出字段和解释的更多信息,请参阅手册中的Explain Results以获取有关MongoDB版本的信息。
使用正则表达式主要关注的是有效使用索引。像/Q10/
这样的未锚定子字符串匹配将需要检查所有索引键(假设候选索引存在,如您的示例中所示)。这是对扫描完整集合数据的改进(如没有索引的情况),但不如能够使用正则表达式前缀搜索检查相关索引键的子集那样理想。
如果您经常搜索子字符串匹配并且字符串有一个共同的模式,那么您可以设计一个更具伸缩性的模式。例如,您可以将Q10
值表示的任何内容保存到单独的字段(例如part_number
)中,您可以使用前缀匹配或完全匹配(非正则表达式)。
为了说明,我使用MongoDB 3.4.2和mongo
shell设置了一些测试数据:
// Needles: strings to search for
db.mod.insert([{number:'Q10'}, {number: 'foo-Q10'}, {number:'Q10-123'}])
// Haystack: some string values to illustrate key comparisons
for (i=0; i<1000; i++) { db.mod.insert({number: "I" + i}) }
db.mod.find({number:{$ regex:/ Q10 /}})。explain(&#39; executionStats&#39;)
winningPlan
是COLLSCAN
(集合扫描),它要求服务器检索集合中的每个文档以执行比较。请注意,原始正则表达式包含不必要的.*
前缀和后缀;这与子字符串匹配是隐含的,因此可以更简洁地编写为/Q10/
。
解释输出的executionStats
部分的重点:
"nReturned": 2,
"totalKeysExamined": 0,
"totalDocsExamined": 1003,
解释输出确认没有检查索引键和1003个文档(此集合中的所有文档)。
db.mod.createIndex({number:1}, {unique: true})
db.mod.find({ number: { $regex: /Q10/}}).explain('executionStats')
winningPlan
仍然是IXSCAN
,但现在必须检查所有1003个索引字符串值以查找子字符串匹配:
"nReturned": 3,
"totalKeysExamined": 1003,
"totalDocsExamined": 3,
db.mod.find({ number: { $regex: /^Q10/}}).explain('executionStats')
winningPlan
是IXSCAN
(索引扫描),需要3次密钥比较和2次文档提取才能返回2个匹配的文档:
"nReturned": 2,
"totalKeysExamined": 3,
"totalDocsExamined": 2,
前缀搜索不等同于前两次搜索,因为它与值foo-Q10
的文档不匹配。但是,这确实说明了更有效的正则表达式搜索。
请注意totalKeysExamined
为3.由于只有2个匹配项,因此预期此值为2可能是合理的,但此度量标准包括与范围外键的任何比较(例如,范围的结束)价值观)。有关详细信息,请参阅Explain Results: keysExamined
。
答案 1 :(得分:1)
启用索引后,对于区分大小写的正则表达式查询,查询遍历整个索引(加载到内存中),然后加载要返回到内存中的匹配文档。它价格昂贵但仍然可能比完整的扫描更好。
对于
/John Doe/
正则表达式,mongo将扫描索引中的整个键集 然后获取匹配的文件。
但是,如果您使用前缀查询:
如果正则表达式是“前缀”,则可能会进一步优化 表达式“,这意味着所有潜在的匹配都以 同一串。这允许MongoDB从中构建“范围” 前缀,仅匹配来自下降的索引中的值 在这个范围内。