我正在尝试对MongoDB集合中的字符串数组执行正则表达式查询。我只能在docs:
中找到此限制$ regex只能在正则表达式时有效地使用索引 有一个锚点作为字符串的开头(即^),是一个 区分大小写的匹配。
让我们做一个测试:
> for (var i=0; i<100000; i++) db.test.insert({f: ['a_0_'+i, 'a_1_2']})
> db.test.count()
100000
> db.test.ensureIndex({f: 1})
> db.test.find({f: /^a_(0)?_12$/ })
{ "_id" : ObjectId("514ac59886f004fe03ef2a96"), "f" : [ "a_0_12", "a_1_2" ] }
> db.test.find({f: /^a_(0)?_12$/ }).explain()
{
"cursor" : "BtreeCursor f_1 multi",
"isMultiKey" : true,
"n" : 1,
"nscannedObjects" : 200000,
"nscanned" : 200000,
"nscannedObjectsAllPlans" : 200000,
"nscannedAllPlans" : 200000,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 482,
"indexBounds" : {
"f" : [
[
"a_",
"a`"
],
[
/^a_(0)?_12$/,
/^a_(0)?_12$/
]
]
},
"server" : "someserver:27017"
}
查询是sloooow。另一方面,此查询是最佳的:(但不适合我的用例)
> db.test.find({f: 'a_0_12' }).explain()
{
"cursor" : "BtreeCursor f_1",
"isMultiKey" : true,
"n" : 1,
"nscannedObjects" : 1,
"nscanned" : 1,
"nscannedObjectsAllPlans" : 1,
"nscannedAllPlans" : 1,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"f" : [
[
"a_0_12",
"a_0_12"
]
]
},
"server" : "someserver:27017"
}
为什么正则表达式查询在有索引时扫描所有(子)记录?我错过了什么?
答案 0 :(得分:1)
您的测试用例有几个对正则表达式和索引使用没有帮助的特性:
/^a_(0)?_12$/
正在查找以a开头,后跟可选&#34; 0&#34;的字符串,因此会导致比较所有索引条目(200k值)。a_1_2
),因此最终将匹配所有文档而不考虑索引由于你有一个多键(数组索引),索引比较的数量实际上更糟而不仅仅是对100k文档进行全表扫描。您可以使用$natural提示进行测试,以查看:
db.test.find({f: /^a_(0|)12$/ }).hint({$natural:1}).explain()
{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"n" : 0,
"nscannedObjects" : 100000,
"nscanned" : 100000,
"nscannedObjectsAllPlans" : 100000,
"nscannedAllPlans" : 100000,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 192,
"indexBounds" : {
},
}
更多随机数据或更具选择性的正则表达式将导致更少的比较。