仕事で全文検索をする機会があったので、そのメモ。いろいろ、形態素解析とか使ってやる方法もあるみたいですが、今回は一番簡単そうだったNgramの方法で。
今回はFULLTEXTインデックスを使っています。文章を分割して、単語ごとにインデックスを張るので検索が早くなるもの、くらいの認識しかしてませんが、ともあれ、ちゃんと動きました。

データベース側の設定

MyISAMエンジンで動いているものにしか、FULLTEXTインデックスは付けられないようなので、エンジンはちゃんと設定しましょう。

新しく作る場合

[code lang=sql]
CREATE TABLE myArticles (
id INT NOT NULL AUTO_INCREMENT,
article TEXT NOT NULL,
PRIMARY KEY(id),
FULLTEXT(article)
) ENGINE=MyISAM;
[/code]

既存のテーブルにFULLTEXTインデックスをつける場合

[code lang=sql]
ALTER TABLE myArticles ADD FULLTEXT( article );
ALTER TABLE myArticles ENGINE=MyISAM;
[/code]

MySQLの設定ファイルの編集

環境によってどこにあるかは変わってくるのですが、

[code lang=text]
mysql –help | grep my.cnf
[/code]

をターミナル上で実行するといくつかパスが出てくると思うので、そこを探してみてください。無かったら、my.cnfファイルを作っても問題ないはずです。

my.cnfファイルの中に次のように書きます。

[code lang=text]
[mysqld]
ft_min_word_len=1
[/code]

デフォルトで4文字未満の検索語では検索できないので、その制約を外しているものになります。例えば、”dog”とか”cat”とかはデフォルトでは4文字制約のために検索出来ません。

※ もしroot権限がないとかで、my.cnfを弄れない場合は一番下に解決策を書いています。

保存するデータを作成

FULLTEXTインデックスはヨーロッパ圏など、単語と単語の間に空白を入れる(分かち書き)言語なら何でもなしにそのまま保存すればいいのですが、日本語などのように、単語で分けて書く習慣のない言語ではちょっと工夫してやることになります。

元の文章「みなさん、こんにちは」「みな なさ さん ん、 、こ こん んに にち ちは」のように分割して保存し、「みなさん」を検索するときには「みな」「なさ」「さん」を含む行を探すという発想です。

まず、「みなさん、こんにちは」を分割しましょう。
phpでは一応、次のようにすると上手く行きました。

[code lang=php]
$s = 'みなさん、こんにちは'
$num = 2;
$result = '';
for($i = 0; $i<mb_strlen($s) – $num + 1; $i ++){
$result .= mb_substr($s, $i, $num) . ' ';
}
echo $result;
//みな なさ さん ん、 、こ こん んに にち ちは
[/code]

もし改行を含むような文字列であれば、まず、その改行で分割してやったりする処理が必要かもしれません。

何はともあれ、ちゃんと保存するデータが作成できました。

検索

マッチ度などを考えず、単純に検索語を含むデータの一覧が返ってきてほしい場合は、いろいろな制約が外れるのでBOOLEAN MODEを使用すると便利です。

[code lang=sql]
SELECT *
FROM myArticles
WHERE MATCH (`keyword`)
AGAINST ("+みな +なさ +さん IN BOOLEAN MODE");
[/code]

AGAINSTの中の文字列を作るには、さっきのPHPのコードを少し改造すると、すぐに作れます。

[code lang=php]
$s = 'みなさん'
$num = 2;
$result = '';
for($i = 0; $i < mb_strlen($s) – $num + 1; $i ++){
$result .= '+' . mb_substr($s, $i, $num) . ' ';
}
echo $result;
//+みな +なさ +さん
[/code]

まとめ

全文検索も結構簡単。ただ、冒頭でも言った通り、いろんなやり方があるので、それぞれの長所と短所を理解しながら使うといいと思います。たとえば、
「みなちゃんのきれいなさらさらの髪を南部さんが掴んだ」1
とかいうわけのわからん文章があったとします。今回用いたN-gramsという方法では、「みな」「なさ」「さん」という文字列が全て入っているものを検索するので、
「”みな”ちゃんのきれい”なさ”らさらの髪を南部”さん”が掴んだ」
となって、検索に引っかかってしまいます。

このように必要ないものが検索に引っかかるのがN-gramsの短所の一つです。詳しく知りたい方はN-gramsなどで検索するといろいろでてくると思います。

my.cnfが弄れないとき

N-gramsを2文字でやってるのが問題なので、4文字にしてやりましょう。例えば「みなさん、こんにちは」を分割するときに余分に”“を追加して、
__みな __なさ __さん __ん、 __、こ __こん __んに __にち __ちは
としてやると、一応4文字になるので、制限を回避出来ます。もちろん検索するときも
+
みな +なさ +さん
で検索することになります。

ただでさえ、データ容量が増える方法なので、これをやると単純計算4倍くらいの大きさになってしまいますが、嫌ならVPSでも借りてくださいということで。


  1. こんな鬼畜なことは自分はしないのでご安心を。