Lucene8:検索ヒット時に元文書該当箇所をハイライトする
というものを作成した。
特に検索時に行う必要はない。このユースケースとしては以下だ。
- ユーザが検索する
- ヒットしたいくつかが一覧される
- ユーザはそのうちの一つを選択する
- この時に初めて元文書をヒット位置と共に表示する。
他で見られるやり方では、検索ヒットしたすべての文書についてハイライトを行い、そのハイライト済データを保持していなければならない。これは無駄である。
ここでは、以下を決めればハイライト済文書を入手できる。
- 検索に使用したクエリ
- ハイライトしたいフィールドのアナライザ
- ハイライトしたいフィールドのコンテンツ
public class RlHighlighter {
final Formatter formatter;
/**
* フォーマッタを指定する
* @param formatter フォーマッタ
*/
public RlHighlighter(Formatter formatter) {
this.formatter = formatter;
}
public RlHighlighter(String color) {
this.formatter = new SimpleHTMLFormatter(
"<span style=\"background-color:" + color + "\">",
"</span>");
}
/**
* クエリ、アナライザ、コンテンツ文字列を指定してハイライト済テキストを得る
* @param query クエリ、
* @param analyzer 該当フィールド用アナライザ
* @param contents 該当フィールドのコンテンツ文字列
* @return ハイライト済テキスト
* @throws Exception
*/
Fragments highlight(Query query, Analyzer analyzer, String contents) throws Exception {
QueryScorer qs =new QueryScorer(query) ;
Highlighter hg = new Highlighter(formatter, qs);
hg.setTextFragmenter(new SimpleSpanFragmenter(qs, -1));
return new Fragments(hg.getBestTextFragments(
analyzer.tokenStream(null, contents),
contents,
false,
10
));
}
public static class Fragments {
public final TextFragment[]fragments;
Fragments(TextFragment[]fragments) {
this.fragments = fragments;
}
@Override
public String toString() {
return Arrays.stream(fragments).map(f->f.toString()).collect(Collectors.joining());
}
}
}