我的任务是不仅根据索引文档的字符串字段的相关性对搜索结果进行排序,而且还根据从给定地理点到与每个被索引文档关联的点之间的距离进行排序。应该提到的是,只有前十名左右匹配的文档应该包含在结果集中。此外,按精确距离排序并不重要,只有距给定点的“距离级别”才是重要的。
从技术上讲,我已经成功地完成了任务。任务的地理部分作为 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 倍。
但是当我从测试切换到真实世界的数据库时,索引中的文档数量从 1000 增加到大约 1000 万,而且还会增加更多(估计近一亿 future ),那么情况就发生了翻天覆地的变化。实际上,我无法再获得任何搜索结果,因为 JVM 内存和处理器耗尽。目前它无法在 JVM 中使用 -Xmx6g 等完成搜索。 当然,我可以为任务购买更好的硬件,但问题可能会通过选择更合适的排序策略来解决。
一种解决方案是完全避免 Lucene 提供的地理排序,如果项目相关性得分相似,则手动对结果集中的前 N 项进行排序。如果没有其他帮助,我将选择这种方式。
但我的问题是是否存在更合适的解决方案。也许我可以以某种方式按等价类(具有相同或相似的分数)拆分结果项,并将地理空间排序仅应用于前几个类?请提出建议。
最佳答案
看看 elasticsearch 如何在 function_score 查询中实现这一点。您可能可以重用他们所做的一些事情。如果我没记错的话,他们也可以选择使用更快但不太准确的距离计算算法。您可能想做类似的事情。
关于java - Lucene 地理距离排序性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25684127/