導入:なぜ「完全一致検索」だけでは不十分なのか
Webアプリケーション開発において、検索機能は必須の要件です。多くの開発者が最初に思い浮かべるのは、SQLの「LIKE」演算子やPHPの「mb_strpos()」を用いた文字列検索でしょう。しかし、これらは「完全一致」を前提としています。「東京は日本の首都です」と「日本の首都は東京です」という文章は、人間にとって意味は同じですが、コンピュータにとっては全くの別物です。検索結果に柔軟性を持たせ、ユーザー体験を向上させるためには、単なる文字列比較を超えた「曖昧検索」の実装が不可欠です。本稿では、形態素解析を用いて文章を単語単位に分解し、適合率によって検索結果を導き出す手法を解説します。
基礎知識:形態素解析とは
形態素解析とは、自然言語の文章を意味を持つ最小単位(形態素)に分割する技術です。例えば「日本の首都」であれば「日本」「の」「首都」というように切り分けます。この分割された単語を比較対象とすることで、語順が変わっても高い精度で検索が可能になります。PHPでこの処理を行うために、今回は「igo-php」のようなライブラリを利用します。
実装・解決策
実装のロジックは以下の3ステップです。
1. 分かち書き:検索キーワードおよび検索対象の全文章を、形態素解析器で単語リスト(配列)に変換します。
2. インデックス作成:あらかじめ文章を単語単位に分解し、検索しやすい形式で保持します。
3. 適合率判定:検索キーワードの単語が、対象文章にどれだけ含まれているかを計算し、しきい値(threshold)を超えていればヒットとみなします。
サンプルプログラム
以下のコードは、形態素解析器「igo-php」を前提とした簡易的な検索クラスの実装例です。
igo = new Igo(“./ipadic”, “UTF-8”);
}
// 文章を形態素解析して配列で返す
function wakati($str) {
return $this->igo->wakati($str);
}
// 検索対象のインデックスを登録
function index($str) {
$words = array_unique($this->wakati($str));
$this->indexes[] = [
‘original’ => $str,
‘words’ => implode(‘ ‘, $words) // 検索用にスペース区切りで保持
];
}
// 検索処理
function search($keyword) {
$searchWords = array_unique($this->wakati($keyword));
$results = [];
$wordCount = count($searchWords);
if ($wordCount == 0) return $results;
foreach ($this->indexes as $index) {
$match = 0;
foreach ($searchWords as $word) {
// インデックス内に単語が含まれているかを確認
if (mb_strpos($index[‘words’], $word) !== false) {
$match++;
}
}
// 適合率がしきい値を超えたら結果に追加
if (($match / $wordCount) >= $this->threshold) {
$results[] = $index[‘original’];
}
}
return $results;
}
}
?>
応用・注意点
実務レベルでこの機能を運用する場合、以下の点に注意してください。
1. パフォーマンスの最適化
サンプルコードはオンメモリで処理していますが、データ量が増えると検索速度が著しく低下します。本番環境では、あらかじめ形態素解析済みの単語をDB(MySQLのFULLTEXTインデックスなど)に格納しておくのが定石です。
2. MySQLの「最低文字数」制限
MySQLで日本語全文検索を行う際、デフォルトでは「4文字以下」の単語が検索対象外になる設定(innodb_ft_min_token_size)がなされていることが多いです。日本語の単語は短いため、設定ファイル(my.cnf)でこの値を「2」程度に調整する必要があります。
3. 辞書の更新
形態素解析は「辞書」の内容に依存します。新語や専門用語を正しく検索させるためには、辞書のメンテナンスが不可欠です。精度を高めたい場合は、mecab-ipadic-neologdのような更新頻度の高い辞書の導入を検討してください。