MySQLの標準機能で日本語を全文検索する(2)
PHPからMySQLへの接続にはPDOを使うことにした。テーブルは、単純にURI, タイトルとコンテントをフィールドとして用意、全文用のフィールドを分けたのは、ngram_prim の内容は ngram_sub にも入り重みが増すようにしてみた(一応、効いてると思われる)。Boolean Mode を使うとスコアは利用できないのでその対策は後述。
テーブル ft
CREATE TABLE ft ( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, uri VARCHAR(255) NULL, title VARCHAR(512) NULL, content MEDIUMTEXT NULL, ngram_prim MEDIUMTEXT NULL, ngram_sub MEDIUMTEXT NULL, PRIMARY KEY(id), FULLTEXT INDEX ft_ftindex(ngram_prim, ngram_sub), INDEX ft_uri_index(uri) );
MySQL接続の設定を別ファイルに切り出す。
config.php
<?php define('DB_NAME', 'tilfin'); define('DB_USER', 'myadmin'); define('DB_PASSWORD', 'myadmin'); define('DB_HOST', 'localhost'); ?>
クラス DBManager はコンストラクタ引数にテーブル名を指定する。insertFullTextIndex メソッドはレコードの追加をする。title を ngram_prim に、content を ngram_sub にそれぞれ全文検索インデックス化して入れている。find メソッドは Boolean Mode で検索を行うが、score を取得するために通常の全文検索も行っている。
db.php
<?php require_once("config.php"); require_once("myindex.php"); class DBManager { var $dbh; var $st; public function DBManager($table) { $this->tablename = $table; $this->dbh = new PDO("mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASSWORD); } public function insertFullTextIndex($uri, $title, $content) { $stmt = $this->dbh->prepare("INSERT INTO ".$this->tablename." (uri, title, content, ngram_prim, ngram_sub) VALUES (:uri, :title, :content, :ngram_prim, :ngram_sub)"); $stmt->bindParam(':uri', $uri); $stmt->bindParam(':title', $title); $stmt->bindParam(':content', $content); $stmt->bindParam(':ngram_prim', $ngramp); $stmt->bindParam(':ngram_sub', $ngrams); $wa = new WordsAnalyzer(); $wa->loadStr($title); $ngramp = $wa->get_fulltext(); $wa->loadStr($content); $ngrams = $wa->get_fulltext(); return $stmt->execute(); } public function insertFullTextIndexPrimary($uri, $title, $content) { $stmt = $this->dbh->prepare("INSERT INTO ".$this->tablename." (uri, title, content, ngram_prim) VALUES (:uri, :title, :content, :ngram_prim)"); $stmt->bindParam(':uri', $uri); $stmt->bindParam(':title', $title); $stmt->bindParam(':content', $content); $stmt->bindParam(':ngram_prim', $ngramp); $wa = new WordsAnalyzer(); $wa->loadStr($title); $ngramp = $wa->get_fulltext(); return $stmt->execute(); } public function find($word){ // bindParam を使うと +, - が使えない。 $fs = new FullTextSearcher(); $matchsql = $fs->search_sql($word); $score = ", MATCH(ngram_prim, ngram_sub) AGAINST (".$fs->getScore().") as score"; $matchsql = "MATCH(ngram_prim, ngram_sub) AGAINST (".$matchsql.")"; $stmt = $this->dbh->prepare("SELECT uri, title, content".$score." FROM ".$this->tablename." WHERE ".$matchsql." ORDER BY score DESC LIMIT 0,50"); $this->fs = $fs; return $stmt; } public function findTitle($title){ $stmt = $this->dbh->prepare("SELECT uri, title, content FROM ".$this->tablename." WHERE title LIKE '%".$title."%'"); return $stmt; } public function getMarkupWords() { return $this->fs->getMarkupWords(); } } ?>