ある文字列が文章内に存在するかを確認するだけなら mb_strpos() で調べることができますが、完全一致なので少しでも言い回しを変えると一致しなくなります。
例えば「東京は日本の首都です」という文章と「日本の首都は東京です」は人間の感覚ではほとんど同じですがコンピュータにとってイコールではありません。
検索に曖昧さを持たせるには、文章を小さな単位に分解し、それがある程度含まれていれば一致したとみなすという手法が一般的で、今回は形態素解析を利用した全文検索を行ってみます。
このサンプルの動作には igo-php が必要です。
過去の記事を参考に環境を準備して下さい。
<?php
require_once 'lib/Igo.php';
$keyword = '東京は日本の首都です';
$text = array(
'これは日本語で書かれた文章です',
'日本の首都は東京です',
'日本語には漢字が使われます。',
'かつては京都が日本の首都でしたが現在は東京です'
);
$fulltext = new FullTextSearch();
// インデックスを作成
foreach($text as $value){
$fulltext->index($value);
}
$results = $fulltext->search($keyword);
print_r($results);
class FullTextSearch
{
private $igo;
private $indexes = array();
private $threshold = 0.8; // 許容するしきい値
function __construct(){
$this->igo = new Igo("./ipadic", "UTF-8");
}
// 分かち書き
function wakati($str){
$arr = $this->igo->wakati($str);
return array_map(array('FullTextSearch', 'remove_space'), $arr);
}
// 検索対象を登録
function index($str){
$arr = array_unique($this->igo->wakati($str));
$this->indexes[] = array(
$str, implode(' ', $arr)
);
}
// インデックスから検索
function search($str){
$words = array_unique($this->wakati($str));
$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;
}
// スペースを取り除く
function remove_space($value){
return str_replace(array(' ', ' '), '', $value);
}
}
結果:
Array
(
[0] => 日本の首都は東京です
[1] => かつては京都が日本の首都でしたが現在は東京です
)
まずはキーワードを分かち書きし、「東京 | は | 日本 | の | 首都 | です」のような配列にします。あとは複数の文章中から一定数の語句を含むものを探して表示します。
本来は検索対象の文章をあらかじめ分かち書きしてデータベースに登録しておきますが、この例はあくまでコンセプトなのでその都度変換しています。
登録されるインデックスは次のような形で格納されています。
Array
(
[0] => Array
(
[0] => これは日本語で書かれた文章です
[1] => これ は 日本語 で 書か れ た 文章 です
)
[1] => Array
(
[0] => 日本の首都は東京です
[1] => 日本 の 首都 は 東京 です
)
[2] => Array
(
[0] => 日本語には漢字が使われます。
[1] => 日本語 に は 漢字 が 使わ れ ます 。
)
[3] => Array
(
[0] => かつては京都が日本の首都でしたが現在は東京です
[1] => かつて は 京都 が 日本 の 首都 でし た 現在 東京 です
)
)
配列 [0] にはオリジナルの文章が、[1] にはスペースで区切った語句が格納されています。
適合率が変数 threshold(しきい値)以上であった場合検索結果に加えるという流れです。
しきい値には0.0~1.0までの小数が利用でき、0.8 のときは単語の 80% 以上が一致すれば検索結果に加えるという意味になります。
Similar Posts:
- [PHP]よくあるエラーメッセージの原因と対処法 2
- [PHP]似た画像を検索して近い順番に並べる(類似画像検索)
- [PHP]複合名詞に対応させて分かち書きをする
- [PHP]RGBをLab色空間の座標に変換する
- [PHP]文章を解析して単語ごとに分解する(形態素解析)
- [PHP]キーワードとなる文字列が何文字目にあるかを全て取得
- [PHP, JS]jQuery UI autocompleteを使った自動補完(候補予測)
- [PHP]正規表現の全体一致は「^~$」だけでは不十分