pg_bigmを動かしてみた
kasa_zipさんのツイート
今朝、いつものようにツイートをチェックしてたら、kasa_zipさんのツイートが目に留まった。
あれ、pg_bigmリリースしたんだ?
むむむ、pg_bigmとな。名前から想像するにPostgreSQL標準の全文検索モジュールpg_trgmのbi-gramバージョンってことだよな、きっと。
2月のPostgreSQL Unconferenceのときに、NTT-DATAさんがpg_trgmの使い方とその問題点を発表していたから、このモジュールはpg_trgmの問題点(1,2文字の条件を与えたときの性能問題)をbi-gramにして解決したものに違いない(飲み会のときにfujii_masaoさんも全文検索系の話題のときに4月になったら云々言ってたような気がするし・・・もう4月になったから書いてもいいよね?)。
ということで、pg_bigmのページから早速DLして動かして見ることにした。
pg_bigmの動作条件
ドキュメントを読むと、RH6系+PostgreSQL 9.1での動作実績はあるらしい。9.2は未検証とのこと。
でも、まあ動くんじゃないかな〜、手元に9.1の環境を作るのもめんどいし〜ということで、とりあえず手元の9.2環境で動かして見ることにした。9.2上での動作検証結果を開発スタッフにフィードバックするのも大事だし・・・。というか単なる面倒くさがりというのが真相だが。
ちなみに手元の環境は
- Windows8
- VMWare 5.0.2
- CentOS 6.3
- PostgreSQL 9.2.4
あ、一つ重大な環境制約があった。
- pg_trgmとpg_bigmは同じデータベース上に共存できない。
むむむ・・・いろんな全文検索系を検証するときに、同じデータを使いたいからこの制約はちょっと面倒臭い。せっかく、pg_hint_planで同じテーブルに張った複数種類のインデクスをHINT句で制御しよう・・・つまりpg_trgm用のインデクスとpg_bigm用のインデクスを両方指定して、HINT句でインデクスを指定して処理を切り替えようと思っていたのに(´・ω・`)
ビルド&インストール
ビルド&インストールは特に問題なし。
普通に USE_PGXS=1 オプションをつけて make と make install check を行うだけ。
テキトーにデータベースを作成してCREATE EXTENSIONで pg_bigm EXTENSIONを登録する。
[ぬこ@横浜]$ psql bigm psql (9.2.4) Type "help" for help. bigm=# CREATE EXTENSION neo4j_fdw pg_bigm bigm=# CREATE EXTENSION pg_bigm ; CREATE EXTENSION bigm=# \dx pg_bigm List of installed extensions Name | Version | Schema | Description ---------+---------+--------+--------------------------------------- pg_bigm | 1.0 | public | text index searching based on bigrams (1 row) bigm=#
pg_bigmの概要
せっかくドキュメントもきちんと用意してあるから読もう。
基本的にはpg_trgmの問題であった
- ヘッダを変更してリビルドしないと日本語が使えない
- (日本語として検索対象となりがちな)1,2文字の文字列に対する検索性能の向上
を改善したもの、という感じだろうか。
元々pg_trgmで持っていた機能は現時点では完全にサポートはしてないけど(GiSTインデクスが未サポートなのと、類似検索への対応がないのがpg_trgmと比較すると気になるところ)、このあたりは今後、改善されていくんだと思う。
あと、pg_trgmに元々なかった(tsearch系にあるような)スコア/スニペット的な機能の追加はない。
なので、基本的にはpg_trgmと同じ適用領域ってことになるのかな。
とりあえず9.2上でうごかしてみた
マニュアル上は、postgresql.conf のshared_preload_librariesの設定が必須、とあるけど独自パラメータを指定しなければ(デフォルトのままであれば)なくても動くんじゃなイカ?
それはともかく、インデクスを設定して検索してみる。
まず、データをロードしてインデクスを作成する。インデクスメソッドにはGINを、演算子クラスには gin_bigm_ops を指定する。
bigm=# CREATE TABLE test (id serial, data text); NOTICE: CREATE TABLE will create implicit sequence "test_id_seq" for serial column "test.id" CREATE TABLE bigm=# COPY test(data) FROM '/tmp/meros.txt'; COPY 90 bigm=# CREATE INDEX test_bigm ON test USING gin (data gin_bigm_ops); CREATE INDEX bigm=#
インデクスは生成できた。次は検索。インデクスが使われていることを確認するために、enable_seqscan=offっておく。
で、とりあえずEXPLAINでプランを確認。
bigm=# EXPLAIN SELECT id, data FROM test WHERE data LIKE '%セリヌンティウス%'; QUERY PLAN -------------------------------------------------------------------------- Bitmap Heap Scan on test (cost=60.08..65.20 rows=10 width=349) Recheck Cond: (data ~~ '%セリヌンティウス%'::text) -> Bitmap Index Scan on test_bigm (cost=0.00..60.08 rows=10 width=0) Index Cond: (data ~~ '%セリヌンティウス%'::text) (4 rows)
で、検索。
bigm=# SELECT id, data FROM test WHERE data LIKE '%セリヌンティウス%' LIMIT 3; id | data ----+----------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------- 3 | メロスには竹馬の友があった。セリヌンティウスである。今は此のシラクスの市で、石工をしている。その友を、これから訪ねてみるつもりなのだ。久 しく逢わなかったのだから、訪ねて行くのが楽しみである。 24 | 「そうです。帰って来るのです。」メロスは必死で言い張った。「私は約束を守ります。私を、三日間だけ許して下さい。妹が、私の帰りを待っている のだ。そんなに私を信じられないならば、よろしい、この市にセリヌンティウスという石工がいます。私の無二の友人だ。あれを、人質としてここに置いて行 こう。私が逃げてしまって、三日目の日暮まで、ここに帰って来なかったら、あの友人を絞め殺して下さい。たのむ、そうして下さい。」 30 | 竹馬の友、セリヌンティウスは、深夜、王城に召された。暴君ディオニスの面前で、佳《よ》き友と佳き友は、二年ぶりで相逢うた。メロスは、友に 一切の事情を語った。セリヌンティウスは無言で首肯《うなず》き、メロスをひしと抱きしめた。友と友の間は、それでよかった。セリヌンティウスは、縄打 たれた。メロスは、すぐに出発した。初夏、満天の星である。 (3 rows) bigm=#
検索できた。
次は検索クエリ関数likequery()を使った例。簡単に言えば、これを使うと%とかのメタ文字を付与しなくてもキーワードの中間一致検索をしてくれる。
bigm=# SELECT id, data FROM test WHERE data LIKE likequery('セリヌンティウス') LIMIT 3; id | data ----+----------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------- 3 | メロスには竹馬の友があった。セリヌンティウスである。今は此のシラクスの市で、石工をしている。その友を、これから訪ねてみるつもりなのだ。久 しく逢わなかったのだから、訪ねて行くのが楽しみである。 24 | 「そうです。帰って来るのです。」メロスは必死で言い張った。「私は約束を守ります。私を、三日間だけ許して下さい。妹が、私の帰りを待っている のだ。そんなに私を信じられないならば、よろしい、この市にセリヌンティウスという石工がいます。私の無二の友人だ。あれを、人質としてここに置いて行 こう。私が逃げてしまって、三日目の日暮まで、ここに帰って来なかったら、あの友人を絞め殺して下さい。たのむ、そうして下さい。」 30 | 竹馬の友、セリヌンティウスは、深夜、王城に召された。暴君ディオニスの面前で、佳《よ》き友と佳き友は、二年ぶりで相逢うた。メロスは、友に 一切の事情を語った。セリヌンティウスは無言で首肯《うなず》き、メロスをひしと抱きしめた。友と友の間は、それでよかった。セリヌンティウスは、縄打 たれた。メロスは、すぐに出発した。初夏、満天の星である。 (3 rows) bigm=#
性能測定
pg_trgmとの性能比較は・・・さすがに自分のPC(しかもVM)上で比較検証するのは微妙な気がするのでパス。
あ、でもpg_trgmとpg_bigmのインデクスサイズの差異については調べてもいいかな・・・?