マソム自宅サーバ構築の記録

  • MySQL5.0とPHP5.2とMeCabでの全文検索
  • 作成日:08/05/30    更新日:09/08/07

前置き

MySQLのFULLTEXTインデックスを使った全文検索は、日本語の場合はそのままでは使えません。
英文の空白を単語の区切りとした構造が前提となっている為だそうです。
しかし、MySQL 4.1.1 以降なら日本語でも単語の区切りに空白を入れる事が出来れば使えるのだそうです。
文字コードはUTF-8を使っていますから問題ありません。その他、SJIS、EUC-JPも使えるようです。

さて、 単語の区切りを見つけて空白等の区切り文字で分ける事を「分かち書き」と言うそうです。
この「分かち書き」をMySQLにお任せ出来るTritonn(全文検索エンジンSennaを組み込んだMySQL)が一番楽そうです。
しかし、MySQLをインストールし直す事になります。

今回は、既に使用しているMySQLをそのまま使う、という状況を想定してみます。
つまり、PHP側で分かち書きを行い、その結果をMySQLに収め、FULLTEXTインデックスを使って全文検索を行うということです。
但し、実装するにはroot権限が必要ですから、共用サーバ等では使えないケースが多そうです。

以下を参考にさせて頂きました。有り難うございます。
Tritonnプロジェクト
MeCab: Yet Another Part-of-Speech and Morphological Analyzer
php_mecab-0.3.0: 某所より引っ越し - 讃容日記
Page2
ウノウラボ PHPとMecabでキーワード自動リンクを実装する
MySQLで全文検索 - FULLTEXTインデックスの基礎知識
Senna 組み込み型全文検索エンジン

PHP側で分かち書きをする流れ

分かち書きにMeCabを利用させて頂くことにしました。MeCabはSennaでも使われている形態素解析エンジンです。
PHPにはMeCabを使う為の拡張モジュールphp_mecabがあるそうです。
これらを使わせて頂いて、
1.MySQLのテーブルに分かち書きしたテキストを入れる項目を追加し、FULLTEXTインデックスを張っておく。
2.レコードの追加&変更前に、PHPでテキストを分かち書きする。
3.バッチ処理等で大量のレコードを追加&変更する時は、FULLTEXTインデックスを処理前に削除し、処理後に張り直すようにする。
4.検索対象文字列を分かち書きして検索。
という運用が出来るか試してみたいと思います。

MeCabとphp_mecabのインストール

まず、MeCabとphp_mecabをインストールしておきます。
php_mecabを使ったPHPでの分かち書きの例はこちら

検索語の文字数を変更する

デフォルトの設定では、4文字未満の検索語は検索の対象にならないのだそうです。
これでは分かち書きした結果、1文字になった語が検索できません。文字数を変更します。
しかし、1文字だと検索語の送り仮名も検索の対象となる為に、一見マッチしていなさそうなもの迄が結果に含まれてしまいます。
そこで、送り仮名を検索の対象から外すことにします。ストップワードリストを記述するファイル名も指定します。
MySQLの設定ファイル /etc/my.cnf に設定を追加します。
$ su
# vi /etc/my.conf
・・・省略・・・
[mysqld]
ft_min_word_len=1
ft_stopword_file = ファイルパス
・・・省略・・・
ストップワードリストファイルを作成します。
# vi ファイルパス









です
ます
ストップワードリストの内容は暫定的なものです。もう少し検証が必要です。
MySQLを再起動します。
# /etc/rc.d/init.d/mysqld restart
この後、phpMyAdminを使ってFULLTEXTインデックスを張りなおしました。

MySQLにFULLTEXTインデックスを張った項目を用意

FULLTEXTインデックスはMyISAM型のテーブルでしか使えません。
つまり、トランザクション処理をしたいテーブルには使えないということです。
一方、FULLTEXTインデックスの更新には時間が掛かるという問題も有ります。

そこで、分かち書きしたテキストだけを収めるMyISAM型のテーブルを別途用意することにしました。
例えば、InnoDB型の商品マスタに、「商品コード(PK)、商品名、値段、在庫数、説明文」の項目があり、「商品名、説明文」で全文検索を行うとします。
検索用商品マスタとして、MyISAM型で「商品コード(PK)、商品名、説明文」というテーブルを用意し、「商品名」と「説明文」にFULLTEXTインデックスを張ります。
この「商品名」と「説明文」には、分かち書きした「商品名」と「説明文」をテキストを収めます。

これで、「商品名」又は「説明文」に変更があった時だけ、FULLTEXTインデックスを張り直す事にもなります。
全文検索は、検索用商品マスタと商品マスタを内部結合して行います。検索用商品マスタは条件文にのみ使い、検索結果には商品マスタの項目を使えば、分かち書きしたテキストが表示されることはありません。
商品受注のトランザクション処理等では商品マスタだけを更新すれば良いので、トランザクション処理にも影響はありませんし、FULLTEXTインデックスを張り直すことにもなりません。

検索用商品マスタの例です。

CREATE TABLE product_srch (
product_id int(10) unsigned NOT NULL,
product_name text collate utf8_unicode_ci NOT NULL,
product_guide text collate utf8_unicode_ci NOT NULL,
createdate datetime NOT NULL,
modifydate datetime NOT NULL,
PRIMARY KEY (product_id),
FULLTEXT KEY product_name (product_name,product_guide)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
検索用SQLの例です。
select
p.product_id, p.product_name, p.price
from product_mst as p
inner join product_srch as s on p.product_id = s.product_id
where match (s.product_name,s.product_guide) against ('今日 は 雨' );
FULLTEXTインデックスを削除するSQLと張り直すSQLの例です。
alter table product_srch drop index fulltext1;

alter table product_srch add fulltext fulltext1 (
product_name ,
product_guide
)

結論

使えそうです。
長めの文章になるとゴミが出るようになりますが、ストップワードの検討でもう少し調整できそうです。
紆余曲折Tips