有效排序mongodb地理空间查询的结果

有效排序mongodb地理空间查询的结果,mongodb,Mongodb,我收集了大量文件,如: { loc: [10.32, 24.34], relevance: 0.434 } 并希望能够高效地执行以下查询: { "loc": {"$geoWithin":{"$box":[[-103,10.1],[-80.43,30.232]]}} } 用任意的盒子 在loc上添加一个2d索引可以使此操作非常快速高效。但是,我现在还想获得最相关的文档: .sort({ relevance: -1 }) 这使得一切都变得缓慢(在任何特定的框中都可能有大量的结果,我只需要前1

我收集了大量文件,如:

{ loc: [10.32, 24.34], relevance: 0.434 }
并希望能够高效地执行以下查询:

 { "loc": {"$geoWithin":{"$box":[[-103,10.1],[-80.43,30.232]]}} }
用任意的盒子

loc
上添加一个2d索引可以使此操作非常快速高效。但是,我现在还想获得最相关的文档:

.sort({ relevance: -1 })
这使得一切都变得缓慢(在任何特定的框中都可能有大量的结果,我只需要前10名左右)


非常感谢任何建议或帮助

当有一个巨大的结果匹配特定的框时,排序操作非常昂贵,所以您肯定要避免它。 尝试在相关性字段上创建单独的索引,并尝试使用它(完全不使用2d索引):通过这种方式,查询将更有效地执行-将逐个扫描符合给定地理框条件的文档(已按相关性排序)。当前10名被发现时,你就很好了

不过,若地理框只匹配集合的一小部分,那个么它可能不会那个么快。在最坏的情况下,它需要扫描整个集合

我建议您创建2个索引(loc vs.relevance),并对应用程序中常见的查询运行测试(使用mongo的提示强制使用所需的索引)


根据您的测试结果,您甚至可能希望添加一些应用程序逻辑,以便如果您知道框很大,您可以使用关联索引运行查询,否则使用loc 2d索引。只是一个想法

在尝试使用对复合键的一部分进行排序时,扫描和顺序值不能为0。不幸的是,目前没有解决您的问题的方法,这与您使用2d索引或其他索引的现象无关

在查询上运行explain命令时,“scanander”的值显示在收集结果后是否需要进行排序。如果为真,则需要在查询后进行排序,如果为假,则不需要进行排序

为了测试这种情况,我以这种方式在示例数据库中创建了一个名为t2的集合:

db.createCollection('t2')
db.t2.ensureIndex({a:1})
db.t2.ensureIndex({b:1})
db.t2.ensureIndex({a:1,b:1})
db.t2.ensureIndex({b:1,a:1})

for(var i=0;i++<200;){db.t2.insert({a:i,b:i+2})}

单个字段上的索引没有多大帮助,因此a_1(不支持排序)和b_1(不支持查询)被删除。a_1_b_1上的索引也不幸运,虽然它的性能比单个a_1差,但mongoDB引擎不会利用与一个“a”值相关的部件以这种方式存储的情况。值得一试的是一个复合索引b_1_a_1,在您的例子中,它将以有序的方式返回结果,因此scanander将为false,我没有测试2d索引,但我假设它将排除仅基于索引值扫描某些文档(这就是为什么在测试中,nscanned高于nscannedObjects)。不幸的是,索引会很大,但仍然比文档小。

您尝试过使用聚合框架吗

两级管道可能会起作用:

  • 一个$match阶段,使用您现有的$GEOIN查询
  • 一个$sort阶段,按照
    相关性进行排序:-1
  • 下面是一个可能的示例:

    db.foo.aggregate(
        {$match: { "loc": {"$geoWithin":{"$box":[[-103,10.1],[-80.43,30.232]]}} }},
        {$sort: {relevance: -1}}
    );
    

    我不确定它将如何运行。但是,即使MongoDB 2.4的性能很差,它在2.6/2.5中可能会有很大的不同,正如2.6将包括的那样。

    如果您需要在框(矩形)内搜索,此解决方案是有效的

    地理空间索引的问题在于,您只能将其放在复合索引的前面(至少mongo 3.2是这样)

    所以我想为什么不创建我自己的“地理空间”索引呢?我只需要在Lat、Lgn(X,Y)上创建一个复合索引,并首先添加排序字段。然后我需要实现在框边界内搜索的逻辑,并特别指示mongo使用它(提示)

    针对您的问题:

    db.collection.createIndex({ "relevance": 1, "loc_x": 1, "loc_y": 1 }, { "background": true } )
    
    逻辑:

    db.collection.find({
        "loc_x": { "$gt": -103, "$lt": -80.43 },
        "loc_y": { "$gt": 10.1, "$lt": 30.232 }
    }).hint("relevance_1_loc_x_1_loc_y_1") // or whatever name you gave it
    
    如果需要包容性结果,请使用$gte$lte

    您不需要使用.sort(),因为它已经被排序,或者如果需要,您可以对相关性进行反向排序


    我遇到的唯一问题是框区域小。查找小区域要比查找大区域花费更多的时间。这就是为什么我保留用于小区域搜索的地理空间索引。

    在创建二维索引时,您是否尝试将
    相关性
    作为索引?谢谢,这两种类型的查询都非常常见,而且都不是真实的要知道该地区是有大量文档还是有少量文档是完全可能/可行的。我想知道的一件事是,我是否可以同时发出两个查询(每个查询使用不同的索引),一旦其中一个返回——终止另一个?我不知道您是否有可能从客户端启动查询并获取其操作id,这样您就可以对其调用killOp。即使它存在,我想您将在mongo实例上获得双重负载,因为当您从成功的查询中获取结果时,会得到失败的查询op id,发送请求终止它,它已经消耗了管理查询本身所需的计算资源+cpu。感谢您在aggregate()函数中展示了一个使用$geoin的示例。这是我能找到的唯一示例。我注意到,使用$sort时,使用aggregate()的速度没有差别还有一个find()函数。但是如果不包括排序,find()的执行速度要比$aggregate快得多。我使用的是MongoDB 2.4.9版。2.6版的信息不错。升级后我会尝试一下。
    db.collection.find({
        "loc_x": { "$gt": -103, "$lt": -80.43 },
        "loc_y": { "$gt": 10.1, "$lt": 30.232 }
    }).hint("relevance_1_loc_x_1_loc_y_1") // or whatever name you gave it