mongodb排序和regex查询

mongodb sort and regex query in efficient way

本文关键字:查询 regex 排序 mongodb      更新时间:2023-09-26
    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 })

monodb中的这个is查询花费了很多时间。如何有效地查询?下面是上面查询的explain((输出。我在收集地点总共有442161份文件。我必须做一些前缀搜索。我已经在(country_lc,docType(、(region_lc,docType(、(city_lc,docType。我的mongo版本是2.4.9。

{
"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"
}

您可以尝试在country_lcregion_lccity_lc字段上创建文本索引:

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

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

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

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 })

(注意:根据文档的结构,这等同于或不等同于您的查询。(

现在我碰巧知道你正在运行2.4.9,这意味着你没有索引分区,$or不能使用排序索引。这个答案可能与2.6中的答案不同。

您的查询存在多个问题,在MongoDB中,除了regex之外,它被认为是一个"坏"查询。

好的,让我们进行排序,在2.4.9中,$or上的排序将不会正确使用索引(https://jira.mongodb.org/browse/SERVER-1205(,这意味着你没有scanAndOrder,但你有一个nscanned计数,它是你收藏大小的数倍。

nscanned准确地说是442161,因为$or实际上是运行许多查询(http://docs.mongodb.org/manual/reference/operator/query/or/#or-子句和索引(,同时其结果被合并然后返回,即使在2.4.9中,您也可以在$or上使用多个索引中看到这一证明。

我看不出你的子句使用了什么索引,但我认为这些索引可能也不适合索引。

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

你可以做一些事情来解决这个问题:

  • 升级至2.6,其中$or和sort可以使用索引
  • 即使在2.6中,由于添加了docType字段,您也可能会遇到问题。您可以尝试在country_lc之后立即将其添加到索引中,但也可以将其添加在索引末尾,它会正常工作,但请记住,它会扫描country_lc中匹配项下方的所有条目
  • 您可能可以利用2.6中的索引intersection来解决每个或子句的这个问题,但正如文档所述(http://docs.mongodb.org/manual/reference/operator/query/or/#or-和排序操作($or特定的索引将被删除,所以我认为这不会起作用

无论以何种方式摇动它,这都是一个可怕的查询,它总是会导致完整的集合扫描,或者至少是完整的索引扫描。

只需在这一份文件上:

{
    "country_lc" : "unitize",
    "region_lc" : "unitmost",
    "city_lc" : "unitleast"
}

查询不可能锚定在索引中的任何位置,因为无论您如何组织作为索引的字段的顺序,由于$or运算符的"排他性"(如排除所有内容(性质,它们都不会匹配。

因此,这些方法或其他组合都不会真正包含一个索引:

db.location.ensureIndex({
    "country_lc" : 1,
    "region_lc" : 1,
    "city_lc" : 1
})
db.location.ensureIndex({
    "region_lc" : 1,
    "city_lc" : 1,
    "country_lc" : 1
})
db.location.ensureIndex({
    "region_lc" : 1,
    "country_lc" : 1,
    "city_lc" : 1
})

即使您.hint()查询,它也不可能找到范围,这也是由于"排他性"性质:

db.location.find(
    { "$or": [
        { "country_lc": /^unit/ },
        { "region_lc": /^unit/ },
        { "city_lc": /^unit/ }
    ]}
).hint(
    { country_lc: 1, region_lc: 1, city_lc: 1 }
).explain()

我所能想到的是,你实际上并不是指"以‘单位’开头的单词",而是指其他的意思。

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

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

编辑

有些人发布了不知情的回复,所以我想我会实际发布建议查询的解释输出:

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

这清楚地表明,即使选择了索引,也不可能匹配索引范围内的任何内容。

关于已经做出的错误评论,这个查询解释响应来自MongoDB的2.6版本。并且也在当前的夜间构建中进行了复制。