CustomScoreQuery
LuceneのAPIでCustomScoreQueryなる便利なものがありました。
いつもお世話になっている関口さんのブログで以前
自分の家から一番近いレストランを探す(地理検索、地図検索)
という記事があったのですが、これをCustomScoreQueryで実装してみました。
→サンプル
FloatFieldSourceを継承したDistanceFunctionを作成します。
DistanceFunctionではgetCachedFieldValuesをオーバーライドし、引数のFieldCacheからキャッシュ値を取得しDocValuesインスタンスを生成します。
cache.getStringsで条件にHITしたDocumentのfieldの値がすべて取得可能です。
#キャッシュするのでINDEXが大きい場合はそれなりのメモリが必要
public DocValues getCachedFieldValues (FieldCache cache, String field, IndexReader reader) throws IOException {
String[] values = cache.getStrings(reader, field);
return new DistanceDocValues(values,d_,x_,y_);
}
DocValuesを継承したDistanceDocValuesではfloatValをオーバーライドします。
ここでは2点間の距離を求めてそれをスコアにしています。
なお、範囲外のデータに関してはスコア0を返すようにします。
Query tq = new TermQuery(new Term(F_VALUE,value)); // すし検索
DistanceFunction func = new DistanceFunction(F_POS,x,y,distance);
ValueSourceQuery vq = new ValueSourceQuery(func);
Query query = new DistanceCustomScoreQuery(tq,vq); //←CustomeScoreQuery
Hits hits = searcher.search( query );
”すし”だけを検索するQueryと、DistanceFunctionを渡したValueSourceQueryとでCustomeScoreQueryインスタンスを生成し、検索すると
1.0 : すし / 3,4
0.7236068 : すし / 5,2
0.3618034 : すし / 9,5
0.19098298 : すし / 1,7
と結果が得られます。
CustomeScoreQuery#customScoreではsubQueryScoreとvalSrcScoreをかけた値がスコアになるようなので、DistanceDocValues#floatValで0スコアを返したデータに関してはヒットせず、自分の家から一番近いレストランを探す(地理検索、地図検索)の
しかし、(たとえスコアを0にしようとも)円の外の座標を持つ文書を「拒否」することはできない。そのため実行結果は次のようになり、座標(9,1)のすし屋も検索されてしまう。
の問題もクリアできました。
なお、DistanceDocValues#floatValはデータがヒットした件数分実行されるので、パフォーマンスに対してクリティカルですので要注意。
Posted in Lucene |
