PHPでMySQLの標準機能で日本語を全文検索する(2)

April 27, 2007

PHPからMySQLへの接続にはPDOを使うことにした。テーブルは、単純にURI, タイトルとコンテントをフィールドとして用意、全文用のフィールドを分けたのは、ngram_prim の内容は ngram_sub にも入り重みが増すようにしてみた(一応、効いてると思われる)。Boolean Mode を使うとスコアは利用できないのでその対策は後述。

テーブル ft

1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
<?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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?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();
}
}
?>
MySQL PHP

tilfin freelance software engineer