Javascript 高效的mongodb排序和正则表达式查询

Javascript 高效的mongodb排序和正则表达式查询,javascript,mongodb,Javascript,Mongodb,这是monodb中的查询,需要花费很多时间。如何有效地查询这个问题?下面是上述查询的explain()输出。我在收集位置总共有442161个文档。我必须做一些前缀搜索。我已经在(国家/地区,docType),(地区,docType),(城市,docType)和(国家/地区,地区,城市)中做了索引。我的mongo版本是2.4.9 db.location.find( { "$or": [ { "country_lc": /^unit/, "docType": "

这是monodb中的查询,需要花费很多时间。如何有效地查询这个问题?下面是上述查询的explain()输出。我在收集位置总共有442161个文档。我必须做一些前缀搜索。我已经在(国家/地区,docType),(地区,docType),(城市,docType)和(国家/地区,地区,城市)中做了索引。我的mongo版本是2.4.9

    db.location.find(
     { "$or": [ 
         { "country_lc": /^unit/, "docType": "country" }, 
         { "region_lc": /^unit/, "docType": "region" }, 
         { "city_lc": /^unit/, "docType": "city" } 
    ]}, 
    { "country": 1, "region": 1, "city": 1, "docType" :1 }
   ).sort({ "country_lc" :1, "region_lc": 1, "city_lc":1 })

不管你用哪种方式摇晃它,这是一个可怕的查询,它总是会导致一个完整的集合扫描,或者至少是一个完整的索引扫描

就这一份文件而言:

{
“国家”——“统一”,
“区域”lc:“最单元”,
“城市信用证”:“至少”
}
由于
$或
运算符的“排他”(排除所有内容)性质,查询不可能锚定在索引中的任何位置,因为无论您如何组织索引字段的顺序,它们都不会匹配

因此,这些方法或其他组合实际上都不会包含索引:

db.location.ensureIndex({
“国家/地区”:1,
“区域:1,
"城市(lc):1
})
db.location.ensureIndex({
“区域:1,
“城市信用证”:1,
“国家/地区”:1
})
db.location.ensureIndex({
“区域:1,
“国家/地区”:1,
"城市(lc):1
})
即使您
.hint()
查询,它也不可能找到范围,这同样是由于“独占”性质:

db.location.find(
{“$or”:[
{“国家/地区”:/^unit/},
{“地区”:/^unit/},
{“城市”:/^unit/}
]}
).提示(
{国家立法会:1,地区立法会:1,城市立法会:1}
).解释
我所能想到的是,你实际上并不是指“以‘单位’开头的单词”,而是指其他的东西

这不仅仅是MongoDB的事情,这对于任何数据库引擎来说都是一件可怕的事情

你可能真的想要一个专门的“文本搜索”引擎

编辑 有些人发布了未经告知的回复,因此我认为我将实际发布建议查询的解释输出:

{
“光标”:“B光标国家/地区/城市/城市”,
“isMultiKey”:错误,
“n”:1,
“非扫描对象”:1,
“未扫描”:1,
“nscannedObjectsAllPlans”:1,
“NSCanendallPlans”:1,
“扫描者”:错误,
“indexOnly”:错误,
“NYELDS”:0,
“跳过”:0,
“毫”:0,
“指数边界”:{
“国家信用证”:[
[
{
“$minElement”:1
},
{
“$maxElement”:1
}
]
],
“地区/地方”:[
[
{
“$minElement”:1
},
{
“$maxElement”:1
}
]
],
“城市信用证”:[
[
{
“$minElement”:1
},
{
“$maxElement”:1
}
]
]
},
“服务器”:“ubuntu:27017”,
“过滤器集”:错误
}
这清楚地表明,即使选择了索引,也不可能匹配索引范围内的任何内容

关于已经发表的错误评论,这个查询解释响应来自MongoDB的2.6版本。并且在当前的夜间版本中也会被重新应用。

您可以尝试在
国家/地区/lc
城市/lc
字段中创建:

{
"cursor" : "BtreeCursor country_lc_1_region_lc_1_city_lc_1",
"isMultiKey" : false,
"n" : 29,
"nscannedObjects" : 76935,
"nscanned" : 442161,
"nscannedObjectsAllPlans" : 76935,
"nscannedAllPlans" : 442161,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 79,
"nChunkSkips" : 0,
"millis" : 81531,
"indexBounds" : {
    "country_lc" : [
        [
            {
                "$minElement" : 1
            },
            {
                "$maxElement" : 1
            }
        ]
    ],
    "region_lc" : [
        [
            {
                "$minElement" : 1
            },
            {
                "$maxElement" : 1
            }
        ]
    ],
    "city_lc" : [
        [
            {
                "$minElement" : 1
            },
            {
                "$maxElement" : 1
            }
        ]
    ]
},
"server" : "prashanta:27017"

}
文本索引是MongoDB 2.4中的一项新功能。添加它们是为了支持集合文档中字符串内容的文本搜索。有关性能提示,请参阅官方文档

此外,您还可以尝试将查询重写为

db.reviews.ensureIndex( { "country_lc": "text" } )
db.reviews.ensureIndex( { "region_lc": "text" } )
db.reviews.ensureIndex( { "city_lc": "text" } )

注意:这是否等同于您的查询,取决于文档的结构。)

现在我碰巧知道您运行的是2.4.9,这意味着您没有索引交叉部分,
$或
s无法使用排序索引。这个答案可能与2.6中的答案不同

您的查询存在多个问题,在MongoDB中,除了正则表达式之外,它被认为是一个“坏”查询

好的,让我们进行排序,在2.4.9中,
$或
上的排序将不会正确使用索引(),这意味着您没有
扫描和排序器
,但您的
扫描计数是集合大小的数倍

精确地说,
nscanned
是442161,因为
$或
实际上是同时运行多个查询(),其结果被合并然后返回,因此即使在2.4.9中,您也可以在
$或
上使用多个索引来证明这一点

我看不出您的子句正在使用什么索引,但我假设这些索引也可能不适合索引

问题是2.4.9根本无法执行
$或
并使用适当的索引进行排序。您必须在索引
$或
或排序之间进行选择,甚至只能部分覆盖查询

要解决此问题,您可以做以下几件事:

  • 升级到2.6,其中
    $或
    和排序可以使用索引
  • 即使在2.6中,由于添加了
    docType
    字段,您也可能会遇到问题。您可以尝试在
    国家/地区\u lc
    之后立即将其添加到索引中,但是您也可以将其添加到索引的末尾,它可以正常工作,但请记住
    db.location.find(
         { "docType": {"$in": [ "country", "region", "city" ]},
           "$or": [
             { "country_lc": /^unit/ },
             { "region_lc": /^unit/ },
             { "city_lc": /^unit/ },
           ]
        }, 
        { "country": 1, "region": 1, "city": 1, "docType" :1 }
       ).sort({ "country_lc" :1, "region_lc": 1, "city_lc":1 })