NaviPlus Engineers' Blog

[Solr] SynonymFilterの挙動まとめ

編集長の佐藤(http://cocky.exblog.jp/)です。

Solrで類義語検索を行う場合良く使われる「SynonymFilter」ですが、いざ類義語を使おうとすると困るのが辞書のフォーマット。結構細かいところで辞書が読み込めなかったりとか問題があるんですね。
なので今回は一通り情報をまとめてみました。
#ざっとさらった程度なので、細かいところの挙動などで下記と違うことがあるかもしれませんが、そのへんはご容赦を。

なお、例によってSolrのバージョンは基本4.2です。最新版の場合挙動が異なる可能性があります。

内部の挙動

まずSynonymFilterですが、Solr 4.2の場合、実体としてはluceneの「FSTSynonymFilterFactory」が呼び出されて、その中で類義語辞書が読み込まれます。

オプションとしては以下のものがあります。schema.xmlでSynonymFilterFactoryを呼ぶときに一緒に指定します。

・ignoreCase
文字通りアルファベットの大文字・小文字の区別を無視するかどうかを指定します。デフォルトはfalse。
・tokenizerFactory
類義語を取り込むときにどのTokenizerを通すかを指定できます。デフォルトは「WhitespaceTokenizer」。
・format
類義語ファイルのフォーマットを選べます。デフォルトは「solr」。
・expand
後述する並列フォーマットのときに類義語を全展開するかどうかを選べます。デフォルトはtrue。

そんでデフォルトだと「SolrSynonymParser」が呼び出されるので、まずはそちらから見ていきます。

SolrSynonymParserの場合

基本的にはSolrのtarballを展開すると、collection1/conf/synonyms.txt にサンプルが置いてあるので、それをベースに処理することになります。
書式は次の2通り。

1. aaa => bbb,ccc

これは「aaa」という単語が来たら、類義語として「bbb」「ccc」の2語を展開するという意味ですね。従って類義語としては「aaa→bbb」「aaa→ccc」の2つのエントリが作られます。

2. aaa,bbb,ccc

これはオプションの「expanded」の値によって挙動が変わります。
expandedの値がfalseだと上記の1.と同じ挙動になりますが、trueの場合は並列の3語をそれぞれ類義語として登録する形になります。
一つ注意する点としては、expanded=trueのとき、類義語エントリとしては「自分自身へのマッピングも作られる」点でしょうか。なので実際のエントリとしては
「aaa→aaa」「aaa→bbb」「aaa→ccc」「bbb→aaa」「bbb→bbb」「bbb→ccc」「ccc→aaa」「ccc→bbb」「ccc→ccc」の9エントリが作られることになります。

ちなみにデフォルトだと、前述したとおり、エントリ生成時に「WhitespaceTokenizer」を通ります。
なので、1.の書式で「aaa => bbb ccc」というようにスペース区切りで単語を登録しても、実用上は問題が生じないと思われます。

あとこれはよく理由がわからんのですが、なぜか文字列の分割にJavaのStringクラスのsplit()メソッドを使わず、わざわざsplit()をクラス内で独自実装してます。
Stringクラスのsplit()は分割対象の文字列指定に正規表現を受け入れるので、それを嫌ったのかもしれませんが…。

WordnetSynonymParserの場合

Luceneにはもう一つ「WordnetSynonymParser」ってのもあります。こちらはパラメータとして「format=”wordnet”」を指定すると使えるようです。

実際コードを読むと、フォーマットについては
http://wordnet.princeton.edu/man/prologdb.5WN.html
を読めって書かれているので開いてみると、元々Prologの世界で使われていたフォーマットなんですね。(筆者はPrologは全くの専門外)

なおステータスがexperimentalなのであくまで自己責任での使用となります。
コードを見ても、まじめにwordnetのformatをparseしているわけではなく、synset_idの取得の時に文字の位置を決め打ちで指定してたり、単語の抽出も単純にシングルクォーテーションの場所を元に判定してたりしていて、本格的に使おうとするとそれなりにコード内部に手を入れないと厳しそうな気がします。

wordnet形式だと、NICTの日本語WordNetなんかがBSDっぽいライセンス(ただしWebサイトで使う場合は名前表示義務あり)で配布されてたりするので、このへんが簡単に使えるようになるとずいぶん楽になると思います。

その他

実際SynonymFilterを使ってみると、結構語数の制限が厳しいようです。SolrのJava VMにどの程度メモリを割り当てるかによって上限は変わると思いますが、「expanded=true」だと実用上は数千語程度が限界ではないか、という感触です。