2012年10月14日日曜日

[MySQL] 日本語全文検索

mysqlの日本語を検索したくてmroongaを試そうとしたら、
Can't open shared library '/usr/lib/mysql/plugin/ha_mroonga.so'
上手く入らない。そもそも32ビット環境ではおすすめされないようなので諦める。
mysqlのFULLTEXTインデックスで全文検索関数を使うことに。

このFULLTEXTインデックスはinnodbでは使えないので、MyISAMのテーブルを用意してJOINして使う。
サンプルとして適当なテーブル用意。
CREATE TABLE `article` (
  `id` int(10) unsigned NOT NULL,
  `body` text,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `body` (`body`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
英語のように空白で単語が分かれていないと使えないのでMecabを使って形態素解析して瀕死に分けてテーブルに入れる。
サンプルのテーブルに適当にデータをいれる。
mysql> select * from article\G
*************************** 1. row ***************************
  id: 1
body: 世界 経済 について 「 著しい 不 確実 性 と 下 振れ リスク が ある 」 と する 声明 を 採択 し た 。
*************************** 2. row ***************************
  id: 2
body: 既存 店 の 営業 を 強化 する とともに 、 新規 出店 も 増やし 、 増収 増益 、 増配 を 目指す 。
*************************** 3. row ***************************
  id: 3
body: しかし アナリスト ら は 、 世界 経済 の 見通し が 弱 含み で ある こと から 、 この 傾向 は 持続 不可能 だ と 警告 し た 。
で、検索すると
mysql> select * from article where match body against('経済' IN BOOLEAN MODE)\GEmpty set (0.00 sec)
上手く検索できない。

ft_min_word_lenのデフォルトが4のため3文字以下は検索できなかったようだ。
my.cnfにft_min_word_len=1を設定して再起動して、またデータを入れ直す。
mysql> select * from article where match body against('経済' IN BOOLEAN MODE)\G
*************************** 1. row ***************************
  id: 1
body: 世界 経済 について 「 著しい 不 確実 性 と 下 振れ リスク が ある 」 と する 声明 を 採択 し た 。
*************************** 2. row ***************************
  id: 3
body: しかし アナリスト ら は 、 世界 経済 の 見通し が 弱 含み で ある こと から 、 この 傾向 は 持続 不可能 だ と 警告 し た 。
これでとりあえず検索はできるようになりました。



ただ約2万行のデータが対象だったので、このくらいなら別にLIKE検索でも良かったのかも。FULLTEXTインデックスの方がLIKE検索より柔軟なORやANDやNOTなどブール全文検索ができるし速いのでメリットはあるが、LIKE検索のお手軽さもパフォーマンスが許容範囲ならやっぱり魅力。要するにケースバイケースで用途に合わせてっていうことで。
mysql> select count(1) from article;
+----------+
| count(1) |
+----------+
|    19361 |
+----------+
1 row in set (0.01 sec)
mysql> select count(1) from article where match body against('経済' in boolean mode) ;
+----------+
| count(1) |
+----------+
|       78 |
+----------+
1 row in set (0.03 sec)
mysql> select count(1) from article where body like '%経済%';
+----------+
| count(1) |
+----------+
|       80 |
+----------+
1 row in set (0.09 sec)
mysql> select count(1) from article where match body against('ビジネス' in boolean mode) ;
+----------+
| count(1) |
+----------+
|      142 |
+----------+
1 row in set (0.06 sec)
mysql> select count(1) from article where body like '%ビジネス%';
+----------+
| count(1) |
+----------+
|      159 |
+----------+
1 row in set (0.08 sec)
mysql> select count(1) from article where match body against('営業' in boolean mode) ;
+----------+
| count(1) |
+----------+
|        9 |
+----------+
1 row in set (0.02 sec)
mysql> select count(1) from article where body like '%営業%';
+----------+
| count(1) |
+----------+
|       10 |
+----------+
1 row in set (0.06 sec)
※件数が違うのは品詞の分け方によって別の単語となってFULLTEXTインデックスの方が件数が少なくなっている。