実例を元にした全文検索エンジン(Groonga)のテーブル設計
Groongaで学ぶ全文検索 2016-02-12に行って来た。今日は、参加者が勉強にと国立国会図書館の書誌情報データを取って来てGroongaに入れてみている、だが今のテーブル設計がいいのか分からないということだったので、それをまず説明してもらって、@ktouさんが各テーブル作成時のオプションなどを説明してくれた。
インデックスを作る前と後で検索結果を比較していて、インデックス作ってそっちで検索すると20倍速くなったということで、インデックスの有用性が証明されてた。
そんなわけで個別に色々説明してくれたのだが、まとまっているわけではないので、幾つか取り上げることにする。
WITH_POSITIONの活躍
今回はインデックステーブルを作る時に、タイトルに対応するインデックスカラムを作る時にCOLUMN_INDEX|WITH_POSITION
と、WITH_POSITION
が指定されていた。これがうまく活きる状況の話があった。
『一、二年生の基礎英作文』というタイトルの本があった。これに対して「一年生」で検索した時に、この本はヒットするだろうか。但し、実際のトークナイザーはTokenBigram
が選ばれていたが、仮にTokenMecab
にしたとする。
タイトルは「一」「、」「二」「年生」「の」……とMeCabはトークナイズした。検索クエリーは「一」「年生」になる。とすると、このタイトルはクエリーのトークン「一」にも「年生」にもヒットするので、ヒットしそうだ。しかししない。なぜならGroongaはこのタイトルカラムではキーワードの位置情報も持っていて、「一」と「年生」がこの順番で隣り合っていないことを知っているからだ。同様の理由で、仮に『二年生の基礎英作文(一)』みたいなタイトルの本があったとしてもヒットしない。このように、「キーワードが文書中でどの位置にあるか」という情報も持たせるオプションがWITH_POSITION
。なので逆に、これを指定していなければ上のクエリーでこの文書はヒットするだろう。
MeCabみたいな形態素解析をせずbi-gramでWITH_POSITION
なし、みたいなカラムを作ると、色々な物がヒットし過ぎて検索には使い物にならないカラムになったりもする。
bi-gramでトークナイズしている時は、一文字では検索できないのか?
bi-gramでトークナイズしている時は、インデックスのキーには二文字の文字列が入っている(文末など場合によっては一文字もある)。そういう時に、検索クエリーが一文字の時には、何もヒットしなくなるのでは? という疑問が出た。
答えはヒットする。今回の実例では、インデックスキーをパトリシリアトライで作っていた(=ハッシュにしていなかった)から。パトリシアトライなら前方一致検索ができるので、一文字のクエリーでも探すことができる。なぜパトリシアトライなら前方一致検索ができるのか? 忘れた。もうパトリシアトライの構造を忘れたので後で見ておく……。
尚、MySQL 5.7から入ったInnoDBの全文検索機能では、bi-gramでトークナイズする設定にすると、実際に一文字での検索ではヒットしなくなるらしい。
テーブルのキーに選ぶべき物
今回は、国会図書館から提供されているTSVのうち、URL、タイトル、著者、出版社をGroongaに入れていた。テーブルとしてはハッシュテーブルを選び、キーにURIを入れていた。これは正しい設計だろうか?
具体的なアプリケーションの要件によって、考慮するべきことがある。
- ハッシュテーブルでいいだろうか?
- いいとして、キーはURLでいいだろうか?
ハッシュテーブルのいい所は、キーが存在していて、そのキーを使ってレコードを一意に特定できる所。逆に言うと特定する必要がない、つまり後からレコードを探して更新や削除をすることが無いのであれば、ハッシュテーブルを使う必要はない。追加しか無いのなら、配列を使ってもいい。
ハッシュテーブルを選んだとして、そのキーカラムのサイズは(現在のGroongaでは)4KiBになっている。普通それならURLを入れても大丈夫だろうと思うが、溢れることがあると@ktouさんは主張していた(何か嫌な思い出がありそうだった)。ので、今回の場合だと、選択したカラムにはなかったが、ISBNを使うのがよさそうとのこと。また、そういう一意かつ短めの物が選べない時は、一意な物のハッシュ値を計算してキーとして入れるといいとのこと。