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();
  }
}
?>