MySQL5.1で日本語全文検索(その5;Mecabで分かち書きしてみるの巻)

前回は、Fulltext Parser ProjectのMecabパーサーと、Bigramパーサーを操作してみた。

Mecabパーサーを使うのであれば、Mecab分かち書きしたテキストをMySQL5.1のデフォルトのパーサーを使って、FULLTEXTインデックスを張ったらどうなるのだろう??
MeCabの処理を分離しておくことで、少なくともMySQL5.1の「公式ルール」通りに動作するはずだし、「なにかいいこと」があるんじゃないか、と思って実験してみた。

  1. あらかじめ、MeCab分かち書きしておく。
  2. MySQL5.1では、MYISAMストレージエンジンでdefault charset=UTF8, collate=utf8_unicode_ciでテーブルを作る。
  3. TEXT型のカラムを作成し、デフォルトのパーサでFULLTEXTインデックスをはる。(型と容量については、MySQL公式ページを参照) TEXT型で2^16のサイズ。
  4. WHERE MATCH AGAINSTで検索する。
mysql> create table nml (c text, fulltext(c)) engine=MYISAM default charset=UTF8 collate=utf8_unicode_ci;

これまでと同様にWikipediaクリスマスローズオミナエシオシロイバナの記述をつかって、これをMecab分かち書きし、まずは、3レコードで実験してみた。

mysql> set names utf8;

mysql> select count(*) from nml where match(c) against('+オミナエシ' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

select count(*) from nml where match(c) against('*オミナ' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('+オミナエシ' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('+オミナエシ +女郎' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('+オミナエシ +女郎花' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('オミナエシ 女郎花' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('+オミナエシ +女郎*' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('+オミナ* +女郎*' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('+オミナ* -女郎*' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('+オミナ* -女郎*' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

思った通りの挙動。
collate を utf8_unicode_ci にしているので、以下のように全角と半角の違いは吸収される。

mysql> select count(*) from nml where match(c) against('+オミナエシ -女郎*' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from nml where match(c) against('+オミナエシ +女郎*' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

最後に、30,000件にデータを増やしてみた。キャッシュを使わない状態で、以下の数字となり、MeCabパーサーを使った場合と同様のレスポンスとなった(2回目以降の検索は、100倍ほど高速になるが、FULLTEXTインデックスだから、評価してもあんまり意味がないように思われる)

mysql> select count(*) from nml where match(c) against(+'オミナエシ' IN BOOLEAN MODE);
+----------+
| count(*) |
+----------+
|    10000 |
+----------+
1 row in set (4.14 sec)