lucene 空间检索
近期对Lucene进行了一些安静的改进,这些改进加在一起使将地理空间距离刻面添加到任何Lucene搜索应用程序中变得异常简单,例如:
< 1 km (147)
< 2 km (579)
< 5 km (2775)
这样的距离面使用户能够快速将其搜索结果过滤到靠近其位置的位置,最近变得尤为重要,因为现在大多数搜索都来自移动智能手机。
在过去,要实现它具有挑战性,因为它是如此动态且如此昂贵:构面数量取决于每个用户的位置,因此无法在用户之间进行缓存和共享,并且空间距离的基础数学很复杂。
但是,Lucene最近进行了几项改进,使这一过程变得异常简单!
首先,Lucene的动态范围 ValueSource
面已被通用化,可以接受任何ValueSource
,而不仅仅是索引中的数字doc values字段。 多亏了最近添加的expressions模块 ,这意味着您可以提供从任意JavaScript表达式计算出的动态范围方面,因为该表达式是使用带有ASM的自定义生成的Java字节码即时编译为ValueSource
。 Lucene的范围刻面现在也更快,它使用段树将每个值快速分配给匹配范围 。
第二,将Haversine距离函数 添加到表达式模块。 该实现对通常昂贵的三角函数使用了令人印象深刻的快速近似,而三角函数的一部分是从Java Optimized Development Kit项目中偷偷猎取的,而又不会牺牲太多的准确性。 逼近在实际中不太可能会起作用,并且存在进一步改善逼近的未解决问题 。
突然,有了这些改进,如果您在每个文档DoubleDocValuesField
经度和纬度索引为DoubleDocValuesField
,并且您知道每个请求的用户的经度/纬度位置,则可以轻松计算构面计数并按任意一组选定的距离进行下钻。
首先,使用纬度/经度字段为文档建立索引:
Document doc = new Document();
doc.add(new DoubleField("latitude", 40.759011, Field.Store.NO));
doc.add(new DoubleField("longitude", -73.9844722, Field.Store.NO));
writer.addDocument(doc);
在搜索时,通过构建调用Haversine函数的动态表达式来获取ValueSource
:
private ValueSource getDistanceValueSource() {
Expression distance;
try {
distance = JavascriptCompiler.compile(
"haversin(40.7143528,-74.0059731,latitude,longitude)");
} catch (ParseException pe) {
// Should not happen
throw new RuntimeException(pe);
}
SimpleBindings bindings = new SimpleBindings();
bindings.add(new SortField("latitude", SortField.Type.DOUBLE));
bindings.add(new SortField("longitude", SortField.Type.DOUBLE));
return distance.getValueSource(bindings);
}
您应该填写用户的位置,而不是上面的有线纬度/经度。
使用该ValueSource
,像这样计算动态构面计数:
FacetsCollector fc = new FacetsCollector();
searcher.search(new MatchAllDocsQuery(), fc);
Facets facets = new DoubleRangeFacetCounts(
"field",
getDistanceValueSource(), fc,
ONE_KM,
TWO_KM,
FIVE_KM,
TEN_KM);
return facets.getTopChildren(10, "field");
通常,您将使用“真实”查询而不是顶层浏览的MatchAllDocsQuery
。 最后,一旦用户选择了要向下钻取的距离,请使用Range.getFilter
方法,然后使用ConstantScoreQuery
将其添加到DrillDownQuery
中:
public TopDocs drillDown(DoubleRange range) throws IOException {
// Passing no baseQuery means we drill down on all
// documents ("browse only"):
DrillDownQuery q = new DrillDownQuery(null);
q.add("field", new ConstantScoreQuery(
range.getFilter(getDistanceValueSource())));
return searcher.search(q, 10);
}
请参阅lucene/demo
模块中的完整源代码 。
当我初次测试此示例时,有一个有趣的bug ,随后对facet API进行了全面修订 ,因此您需要等待Lucene 4.7版本,或者仅使用最新的4.x源代码来获得此示例。加工。
尽管此示例很简单且可以正常运行,但仍有可能实现一些明显的性能改进,例如使用边界框作为快速匹配项,以避免计算明显超出可能的向下钻取范围(补丁)的点击量的Haversine欢迎!)。 即便如此,对于Lucene的刻面而言,这仍然是一个不错的进步,而Lucene的地理空间距离刻面如此简单令人惊讶。
lucene 空间检索