Luceneで日本語ハイライト

Lucene は、日本語が使える N-Gram の CJKAnalyzer が利用でき(ソースコードリポジトリのcontribから取得)、日本語のIndexも作成できた。とりあえず、検索とかもテストしたが使える感じだ。うまく、検索できるならば、サマリも先頭からとかではなく、キーワードの前後を抜き出したいのが人情だろう。ということで、同じように ソースコードリポジトリから、ハイライトを行えるクラス Highlighter というのを持ってきた。とりあえず、サンプルにあるような形で適用してみたのだが、英語はいいのだが、日本語では、キーワードだけでなく、連続する日本語もハイライトされてしまい、そのままでは使えないかんじだった(注: デフォルトのFormatterを使ってたからなんですけどね...。調べた結果わかりました。)。

不満ではあったけど、英語では使えるので他の事をやっていたのだが、そろそろ真剣に調べるかなと思って調べてみた。

そして、分かったことは、Highlighter.getBestTextFragments() というメソッドの中で、切り出したトークンが、今まで出てきたトークンと重なっているかどうかを調べている個所があるようだ。そこのデフォルトの Highlighter の動作では、その重なっている個所を1つの TokenGroup として扱うようになっている。英語の場合は、単語区切だろうから、どうやって利用するのかちょっと分からないのだが、あいまい検索などでもしかしたら、使うのかもしれない。

StandardAnalyzer というやつではなく、CJKAnalyzer を利用すると次のような Token 分割になっている

token[0] : テス
token[1] : スト
token[2] : ト構
token[3] : 構造
...

これは、1つの Token が2文字で出来ていて、それぞれ1文字づつずれる感じでTokenが取り出される(2-gram で分割されるみたいだ)。この為、日本語では、Token が分割できない(前回出てきたTokenに今評価しているTokenの一部が含まれる状態)為、英単語が出てきたり、なにか区切り文字がでるまで、ずらずら続くことになる。

このチェックをやっているのは、Highlighter.getBestTextFragments() 内で、tokenGroup.isDistinct(token) というようなメソッドを呼び出しているところだ。

Highlighterのコードを変更することは避けたい為、ここをいじるのはあまりやりたくない。なので、もう少しコードを読んでみると、Formatter を実装したクラス(HTMLのboldとかのタグでハイライトをする為のクラス) に渡されるテキストが、オリジナルテキストと、TokenGroup であることから、なんとかなりそうだ。

デフォルトの動作だったので、SimpleHTMLFormatter というクラスを利用していたのだが、このクラスの実装をみると単純に TokenGroup に含まれるテキストの前後を ... でサンドイッチしただけだった。その結果、例えば、「テスト構造」というテキストを「構造」という単語で検索すると、「テスト」とかも含めて ハイライトされてしまう。そして、見た目がいまいちになる。

よくよくコードを見たりデバッガでみると、キーワードが見つかったところは、ちゃんと score が 0 ではなくなっていることが見えてくる。ということは、この値を区別することでなんとかなりそうだ。そして、スコアは、TokenGroup に設定されているので、そこから何とかできる。つまり、Formatter を自分で実装すれば、何とかなりそうだ。

渡されてくる文字には、キーワードが複数入っていることもあるので、それを気をつけながら、score が 0 以上のものをハイライトさせるようにしてみたら、キーワードだけハイライトする事ができた。単純な検索でのテストしかしていないが、まぁ問題ないだろう。後は、テストをいくつかやって、問題がでたら考えることにしよう。

と、調べてたら、別のエラーが、、、TokenGroup.addToken で ArrayIndexOutOfBoundsException が投げられてる...。これは、ソースいじらないとだめかもなぁ。。また、時間を見て調べてみよう。