【问题标题】:Lucene spatial, accuracyLucene 空间、精度
【发布时间】:2011-07-12 16:28:34
【问题描述】:

我正在关注“Lucene in Action”第 308-315 页中的示例,该示例描述了 Lucene Spatial。我正在使用 lucene 2.9.4。我使用http://geocoder.us/service/distance 端点来计算一些位置之间的距离,然后编写单元测试来验证索引是否可以找到给定半径内的位置。

我想知道我可以期望 lucene 有多准确。例如,如果我给出半径 10.0 并且我的纬度/经度点之间的距离是 9.99 英里,它是否能够在所有情况下找到这个位置?

提示这个问题的原因是,我发现搜索对于小半径值(例如 10.0 或更小)非常准确,而对于较大的值(例如 r=25.0)则不准确。

有什么我可能做错了吗?搜索者是否有可能选择一个没有给定半径的所有纬度/经度的层?我的理解是它选择了保证所有点都在半径内的最小层,即层算法只是一种优化。

编辑: 我还发现了这个:https://issues.apache.org/jira/browse/LUCENE-2519 和这里明显固定的代码:http://code.google.com/p/spatial-search-lucene/source/browse/trunk/src/main/java/org/apache/lucene/spatial/tier/projection/SinusoidalProjector.java?r=38,但是当我修补我的代码以使用固定的 SinusoidalProjector 时,我的索引在所有情况下都返回零广告。

这并没有给我很大的信心:

http://www.lucidimagination.com/blog/2010/07/20/update-spatial-search-in-apache-lucene-and-solr/

http://www.lucidimagination.com/search/document/c32e81783642df47/spatial_rethinking_cartesian_tiers_implementation#c32e81783642df47

这似乎表明整个代码都存在黑客攻击,仅仅修补 SinusoidalProjector 是不够的。

【问题讨论】:

    标签: java lucene


    【解决方案1】:

    我花了一些时间查看源代码,我想我明白出了什么问题。首先,我做了一个错误的假设,即 geocoder.us 计算的距离与 lucene 内部计算的点之间的距离相同。这些值很接近,但并不准确。所以我转而通过调用 lucene's 来计算纬度/经度对之间的距离

    double distance = DistanceUtils.getInstance().getDistanceMi(lat1,lon1,lat2,lon2);
    

    接下来我研究了DistanceQueryBuilder 类http://grepcode.com/file/repo1.maven.org/maven2/org.apache.lucene/lucene-spatial/2.9.4/org/apache/lucene/spatial/tier/DistanceQueryBuilder.java?av=f,我认为它有一个错误。

    它计算边界框以获取笛卡尔层,如下所示:

    CartesianPolyFilterBuilder cpf = new CartesianPolyFilterBuilder(tierFieldPrefix);
    Filter cartesianFilter = cpf.getBoundingArea(lat, lng, miles);
    

    通过查看 LLRect.createBox http://grepcode.com/file/repo1.maven.org/maven2/org.apache.lucene/lucene-spatial/2.9.4/org/apache/lucene/spatial/geometry/shape/LLRect.java#LLRect.createBox%28org.apache.lucene.spatial.geometry.LatLng%2Cdouble%2Cdouble%29 很清楚,getBoudningArea 的第三个参数将被视为边界框的完整宽度/高度。所以传递半径值会导致边界框太小。

    解决方法是提供一个替代版本的 DistanceQueryBuilder 来执行此操作:

    Filter cartesianFilter = cpf.getBoundingArea(lat,lng,miles*2);
    

    这似乎有效。我仍然相信 DistanceApproximation http://grepcode.com/file/repo1.maven.org/maven2/org.apache.lucene/lucene-spatial/2.9.4/org/apache/lucene/spatial/geometry/shape/DistanceApproximation.java#DistanceApproximation.getMilesPerLngDeg%28double%29 被破坏了,因为似乎以下操作应该是可逆的,但它们不是:

    // similar to implementation of DistanceUtils.getBoundary():
    double milesPerLng = DistanceApproximation.getMilesPerLngDeg(lat);
    double milesPerLat = DistanceApproximation.getMilesperLatDeg();
    
    
    double lngDelta = radius / milesPerLng;
    double latDelta = radius / milesPerLat;
    
    // Now it seems like this should be roughly true:
    assertEquals(radius, DistanceUtils.getInstance().getDistanceMi(lat,lng,lat,lng+lngDelta));
    assertEquals(radius, DistanceUtils.getInstance().getDistanceMi(lat,lng,lat+latDelta,lng));
    

    但事实并非如此。例如,当上面的代码给出 lat=34、lng=-118 和 radius=25(而不是断言我只是打印结果)时,我得到:

    Lng delta: 0.36142327178505024, dist: 20.725929003138496
    Lat delta: 0.4359569489852007, dist: 30.155567734407825
    

    我猜测代码之所以有效,是因为在选择边界框后选择的笛卡尔层将导致区域比边界框大一些。但我认为这不会得到保证。

    我希望对此有更多了解的人可以发表评论,因为这些只是在研究代码一个下午后的观察。我确实注意到 lucene spatial 的最新代码位于 googlecode 上:http://code.google.com/p/spatial-search-lucene/,并且实现似乎发生了重大变化,但我没有深入挖掘细节。

    【讨论】:

    • Lucene 4 空间确实是基于code.google.com/p/spatial-search-lucene 并且有很大不同。我建议您升级到 Lucene 4。v3 中的空间模块非常有问题/不可信且未维护,因此被抛弃了。
    • 谢谢...我不再在同一家公司工作,但我正在使用 solr 4 进行当前演出的项目。
    【解决方案2】:

    他们在 Lucene 3.5.0 中修复了这个问题。远距离现在和小距离一样有效

    【讨论】:

      猜你喜欢
      • 2015-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-05
      • 2011-07-07
      • 1970-01-01
      相关资源
      最近更新 更多