n-gram 検索したいので、そういう感じで。
MySQL 4.1 リファレンスマニュアル :: 6.8 MySQL 全文検索 を参考に進めていく。

# いくつもの個所でハマったので、記憶を頼りに備忘録。

ft_min_word_len に単語の最小長を指定

MySQL 変数 ft_min_word_len に単語の最小長を指定する。
今回は、1-gram かつ 2-gram で検索したいので my.cnf の [mysqld] セクションに ft_min_word_len=1 をセットする。つまり、1つ以上の文字で構成される単語を検索可能な設定。

インデックスを作成するワードの最小長は、MySQL 変数 ft_min_word_len で定義される。 See 項4.6.8.4. 「SHOW VARIABLES」。 (この変数は MySQL バージョン 4.0 以降でのみ使用できる)。 デフォルト値は 4 文字。 この値を必要な値に変更して、FULLTEXT インデックスを再ビルドする。 たとえば、3 文字のワードを検索可能にするには、オプションファイルに以下の行を書き込むことによってこの変数を設定する。

[mysqld]
ft_min_word_len=3

その後、サーバを再起動して、FULLTEXT インデックスを再ビルドする。

MySQL 4.1 リファレンスマニュアル :: 6.8.2 MySQL 全文検索の調整
ft_min_word_len FULLTEXT インデックス内の単語の最短長。 注意: この変数を変更後、FULLTEXT インデックスを再ビルドする必要がある。このオプションは MySQL 4.0 で導入された。

MySQL 4.1 リファレンスマニュアル :: 4.6.8.4 SHOW VARIABLES

テーブル作成時に fulltext 指定

テーブルを作る。fulltext にてフルテキストインデックス指定する。


create table hogetable (
  id       int          not null auto_increment,
  c1       varchar(255) not null default '',
  hint     text         not null default '',
  primary  key          (id),
  fulltext key i_hint   (hint)
) ENGINE=MyISAM;

MyISAM ストレージエンジンでなければ全文検索はできない。
UTF-8 で日本語もそのまま使える。

# 少し前までは日本語が使えなかったらしく、何らかの英数字変換をして対処する人もいたようだ。
# もしかしたら、今でもその方法のほうが無難かもしれない。日本語処理は落とし穴がありそうなので。

* 全文検索は MyISAM テーブルでのみ可能。
* 全文検索は UCS-2 では使用できない(しかし、MySQL 4.1.1 以降では、UTF-8 で機能する)。
* MATCH() 関数のパラメータはすべて、同じ FULLTEXT インデックスの一部を成す同じテーブルのカラムでなければならない(IN BOOLEAN MODE で MATCH() を実行する場合を除く)。
* FULLTEXT インデックスのカラムはすべて同じキャラクタセットを使用していなければならない。
* MATCH() のカラムリストはテーブルの一部の FULLTEXT インデックス定義のカラムリストと正確に一致していなければならない(IN BOOLEAN MODE で MATCH() を実行する場合を除く)。
* AGAINST() の引数は定数文字列でなければならない。

MySQL 4.1 リファレンスマニュアル :: 6.8.1 全文検索における制約

n-gram データの挿入

データを入れる。fulltext のカラムには半角空白文字で区切った単語を入れる。
今回は、1文字と2文字の単語に分割して入れておく。

2文字分割: マイエスキューエル -> マイ イエ エス スキ キュ ュー ーエ エル
1文字分割: マイエスキューエル -> マ イ エ ス キ ュ ー エ ル

たとえば、こんな感じでデータを入れる。

insert into hogetable (c1, hint) values ('マイエスキューエルはグレイトな感じですね', 'マイ イエ エス スキ キュ ュー ーエ エル ルは はグ グレ レイ イト トな な感 感じ じで です すね。 ね。 マ イ エ ス キ ュ ー エ ル は グ レ イ ト な 感 じ で す ね 。')

n-gram 全文検索

全文検索する例:


select c1 from hogetable where match (hint) against ('マイ' in boolean mode) limit 10
select c1 from hogetable where match (hint) against ('+マイ +マ' in boolean mode)
select c1 from hogetable where match (hint) against ('+マイ +イエ +エス +スキ +キュ +ュー +ーエ +エル' in boolean mode)
select c1 from hogetable where match (hint) against ('+マイ +感' in boolean mode)

動的にクエリーを発行するようなプログラムから接続するときは、検索文字列をうまいこと2文字分割や1文字分割する必要がある。

入れるデータや検索文字列を、2文字単位・1文字単位で分割するようなコードを Java で書いてみる。


// bi-gram
private static String[] createBiGramTokens(String s){
 
  if(s.length() < 2){
    //return new String[]{s}; // 迷った
    return new String[0];
  }
 
  String[] gram = new String[s.length() - 1];
  for (int i = 0; i < s.length() - 1; i++) {
    gram[i] = s.substring(i, i+2);
  }
  return gram;
}
 
//mono-gram?
private static String[] createMonoGramTokens(String s){
  String[] tokens = new String[s.length()];
  for(int i=0; i<s.length(); i++){
    tokens[i] = s.charAt(i) + "";
  }
  return tokens;
}

ブール値の全文検索機能(in boolean mode)では、+ - < > ( ) ~ * " といった演算子が使える。

バージョン 4.0.1 以降、MySQL では、IN BOOLEAN MODE 修飾子を使用してブール値の全文検索も実行できます。

MySQL 4.1 リファレンスマニュアル :: 6.8 MySQL 全文検索

自分以外のユーザがいるプログラムだったら、意図しない文字が入らないように処理する必要がある。特に、in boolean mode の演算子文字 + - < > ( ) ~ * "
日本語しか検索しないのであれば、いっそのこと制御文字英数半角はまとめて消してしまっても良いかも。
こんな風に。


private static String clear(String s){
  StringBuffer buf = new StringBuffer();
  for(int i=0; i<s.length(); i++){
    char ch = s.charAt(i);
    if(ch > 256){
        buf.append(ch);
    }
  }
  return buf.toString();
}

参考資料

ちょっと脱線

n-gram の n は文字の数だと思っていたが、

グラムは別に文字でなくても、単語でも良いのです。
(中略)
そこも、文字ではなく単語単位なら、2単語ずつ分けるので bi-gram に
なるはずです。

[Namazu-devel-ja 742] Re: フレーズ検索って N-gram ですよね

単語でも良い?

その namazu のメーリングリストで 北 研二, 津田 和彦, 獅々堀 正幹 著 / 情報検索アルゴリズム という書籍が紹介されていた。読みたい……と思ったが、なんか見たことあるような → My読了本リストを検索 → 2004年に読んでいることが発覚。……さて。

追記: 2007-08-31

参考になりそうなページをメモ。

-MySQLで全文検索 - FULLTEXTインデックスの基礎知識|blog|たたみラボ
-MySQL FULLTEXT + Ngram : LIKE検索より数十倍高速な、お手軽 日本語全文検索 について|blog|たたみラボ


コメント

きょうづらねこにゃんが演算されたみたい…

きのうNI-Lab.で、実行したかったみたい。
さてここで変数へ分割する?
さてきょうづらねこにゃんは、NI-Lab.が日本語みたいな使用したかったの♪
しかしNI-Lab.が分割したかった。

tags: zlashdot Database FullTextSearch MySQL

Posted by NI-Lab. (@nilab)