2014-09-05 91 views
4

我有一项任务,不仅可以通过索引文档的字符串字段的相关性对搜索结果进行排序,也可以通过从给定地理点到与每个索引文档相关的点的距离进行排序。应该提到的是,只有前十个左右匹配的文档应该包含在结果集中。按精确距离排序并不重要,只有给定点的“距离水平”才是重要的。Lucene地理距离排序性能

从技术上讲,我已经成功实施了这项任务。任务的地理部分被实现为CustomScoreQuery派生类:

private static class DistanceQuery extends CustomScoreQuery { 

    public DistanceQuery(final Query _subQuery, final SpatialStrategy _strategy, final Point _bp) { 
    super(_subQuery, new FunctionQuery(_strategy.makeDistanceValueSource(_bp))); 
    } 

    @Override 
    protected CustomScoreProvider getCustomScoreProvider(AtomicReaderContext _context) throws IOException { 
    return new CustomScoreProvider(_context) { 
     @Override 
     public float customScore(int _doc, float _subQueryScore, float _valSrcScore) throws IOException { 
     // the spatial strategies makeDistanceValueSource creates a ValueSource which score varies from almost 0 for nearby points to 2.7-2.8 for distant points 
     // so I voluntarily chosen 2 as the normalization factor and increase subQueryScore for that factor at max; 
     logger.debug("customScore for document {}: [subQuery={}, valScore={}", this.context.reader().document(_doc).getField(IndexedField.id.name()).numericValue().toString(), _subQueryScore, _valSrcScore); 
     return (_valSrcScore > 2 || _valSrcScore < 0) ? _subQueryScore : _subQueryScore + (2 - _valSrcScore); 
     } 
    }; 
    } 
} 

和包裹给定的“文本”的查询与该地理空间“增强”。

一般来说,选择的策略给了我相当合理的结果。可以看到,最终得分略高于最初的查询得分(最大为2)。通过十几个甚至更多的典型结果评分,这种地理空间增值工作仅仅是对“其他类似文档进行”后处理“的方式。

在索引中有几百或几千个测试文档,包装查询的性能也足够好。每次搜索大约需要10-50毫秒,这比解开查询慢了2-5倍。

但是,当我从测试切换到真实世界的数据库,并且索引中的文档数量从一千个增加到大约一千万个,并且将增加更多(估计一个数百万的文档不久的将来),情况发生了巨大变化。事实上,我无法再获得任何搜索结果,因为JVM会耗尽内存和处理器。目前,它无法使用-Xmx6g等在JVM中完成搜索。 当然,我可以为这项任务购买更好的硬件,但这个问题可能通过选择更合适的分类策略来解决。

一个解决方案是完全避免由Lucene提供的地理排序,如果项目相关性分数相似,则手动对结果集的前N项进行排序。如果没有其他帮助,我会选择这种方式。

但我的问题是是否存在更适当的解决方案。也许我能以某种方式将结果项目按等效类别(具有相同或相似的分数)进行拆分,并将地理空间排序仅应用于前几个类别?请建议。

回答

1

看看elasticsearch如何在function_score查询中实现这一点。你可以重复使用他们所做的一些事情。如果我没有记错的话,他们可以选择使用更快但更不准确的距离计算算法。你可能想要做类似的事情。

+0

好的,谢谢!我要评估这种可能性,然后写一个答案,并批准你的答案,希望在一天左右。 – user3159253 2014-09-08 05:11:25

0

我在使用其他CustomScoreProvider为DistanceQuery:

public class DistanceQueryScoreProvider extends CustomScoreProvider { 

    private double x; 
    private double y; 

    public DistanceQueryScoreProvider(LeafReaderContext context, double x, double y) { 
     super(context); 
     this.x = x; 
     this.y = y; 
    } 

    @Override 
    public float customScore(int doc, float subQueryScore, float valSrcScore) throws IOException { 
     Document d = context.reader().document(doc); 
     double geomX = d.getField(Consts.GEOM_X_FIELD).numericValue().doubleValue(); 
     double geomY = d.getField(Consts.GEOM_Y_FIELD).numericValue().doubleValue(); 
     double deglen = 110.25; 
     double deltaX = geomY - y; 
     double deltaY = (geomX - x) * Math.cos(y); 
     return -Double.valueOf(deglen * Math.sqrt(deltaX * deltaX + deltaY * deltaY)).floatValue(); 
    } 
} 

Elasticsearch从Sorting by Distanceplane距离函数implementation较慢,比上述代码功能customScore。此功能是基于文章Geographic distance can be simple and fast

user3159253实现的,也许您有此线程的答案?