導入: なぜ形態素解析による検索が必要なのか
PHPの mb_strpos() などを用いた文字列検索は、厳密な一致には適していますが、「言い回しが変わるとヒットしない」という弱点があります。例えば、「東京は日本の首都です」と「日本の首都は東京です」は、人間にとっては同じ意味ですが、コンピュータにとっては別物です。
実務レベルで「検索の利便性」を向上させるには、文章を意味のある最小単位に分解し、キーワードの一致率を算出する「形態素解析」を用いた検索手法が非常に有効です。本稿では、PHPにおける簡易的な全文検索エンジンの概念と実装方法を解説します。
基礎知識: 形態素解析と分かち書き
形態素解析とは、自然言語を意味を持つ最小単位(形態素)に分割する技術です。日本語には英語のようなスペースがないため、検索エンジンがキーワードを識別するには、この解析が不可欠です。
この分解された単語をスペースで区切った状態を「分かち書き」と呼びます。今回の実装では、形態素解析ライブラリ igo-php を使用し、対象テキストを単語リストへ変換して比較を行います。
実装: 検索ロジックの構築
今回のロジックは以下の手順で行います。
1. 検索対象の文章を形態素解析で分解(分かち書き)し、インデックスとして保持。
2. 検索キーワードも同様に分解。
3. キーワードに含まれる単語が、対象文章のインデックス内にどれだけ含まれているかをスコアリング。
4. 設定したしきい値(threshold)を超えたものを検索結果として抽出。
サンプルプログラム
以下のコードは、簡易的な全文検索クラスの実装例です。実行には別途 igo-php ライブラリの導入が必要です。
igo = new Igo(“./ipadic”, “UTF-8”);
}
// テキストを形態素に分解して登録するメソッド
public function index($str) {
$arr = array_unique($this->igo->wakati($str));
$this->indexes[] = array($str, implode(‘ ‘, $arr));
}
// 検索実行メソッド
public function search($keyword) {
$words = array_unique($this->igo->wakati($keyword));
$results = array();
$wordCount = count($words);
if ($wordCount == 0) return $results;
foreach ($this->indexes as $index) {
$match = 0;
foreach ($words as $word) {
// インデックス内に単語が含まれているかチェック
if (mb_strpos($index[1], $word) !== false) {
$match++;
}
}
// 一致率がしきい値を超えたら結果に追加
if ($match / $wordCount >= $this->threshold) {
$results[] = $index[0];
}
}
return $results;
}
}
// 使用例
$search = new FullTextSearch();
$search->index(‘日本の首都は東京です’);
$search->index(‘かつては京都が日本の首都でした’);
print_r($search->search(‘東京は日本の首都’));
?>
応用・注意点: 実務での活用に向けて
本稿のサンプルはオンメモリで動作するコンセプトモデルです。実務環境では以下の点に注意してください。
1. パフォーマンスの考慮
検索のたびに形態素解析を行うのは負荷が高いため、事前(インサート時)に分かち書きした文字列をデータベースに保存しておくのが定石です。
2. MySQLのFULLTEXTインデックス
MySQLで本格的に行う場合は、MATCH … AGAINST 構文を使用します。その際、日本語環境ではサーバー設定の ft_min_word_len がネックになります。日本語の単語は短いため、デフォルトの「4」のままでは検索にヒットしないことが多々あります。必ず設定ファイル(my.cnf)で値を「2」程度に調整してください。
3. 辞書の更新
形態素解析は辞書データに依存します。固有名詞や最新のトレンドワードを検索対象に含めたい場合は、辞書の定期的なアップデートが運用上の重要課題となります。