mysqlftppc 紹介
DESCRIPTION
mysqlftppc の背景について。プロジェクトページはこちら。 http://mysqlftppc.wiki.sourceforge.net/TRANSCRIPT
mysqlftppc 紹介
川井 浩陽
2009-02-12
まず
mysqlftppc = MYSQL Full-Text Parser Plugin Collection
サーバに組み込んで使います。Cで書かれています。
http://mysqlftppc.wiki.sourceforge.net/
MySQL full-text search
SELECT * FROM hoge WHERE MATCH(t) AGAINST(“hogehoge”)
SQL の関数として導入されています。
http://dev.mysql.com/doc/refman/5.1/en/fulltext-search.html
MySQL full-text search
Natural language mode
「この文章に近い文章を見つける」というモード
二つの「文章」を比較して検索結果に含めるかどうかを判断する。
Query Expansion 機能(「ひょっとして~」)
SELECT * FROM hoge WHERE MATCH(t)AGAINST(“The quick brown fox jumps over the lazy dog”);
SELECT * FROM hoge WHERE MATCH(t)AGAINST(“The quick brown fox jumps over the lazy dog”WITH QUERY EXPANSION);
MySQL full-text search
Boolean mode
「この単語は含むがこの単語は含まないものを見つける」というモード。
文章に含まれるトークン群が条件式を満たすかどうか
条件式を表現するための Boolean syntax
SELECT * FROM hoge WHERE MATCH(t)AGAINST(“+hoge -geho” IN BOOLEAN MODE);
SELECT * FROM hoge WHERE MATCH(t)AGAINST(“+(hoge geho) -ab” IN BOOLEAN MODE);
しかし問題もある
トークンの抽出ロジック
実用上使えるのは latin1 のみ。
内部に ctypes という文字種別の辞書を持っている。
「普通の文字*1」の連続を探し出してトークンとする。
この辞書はUnicode以前に整備されたもので、Unicodeにある文字種定義とは全くの別物。
STOPWORDS頻出単語で注目すべきではなく、無視する単語のこと。
基礎にしている参照元のリスト(英語)はあるが、作者の趣味で変更されている(デフォルト)。
ft_min_word_len4文字(デフォルト)より短い文字列は全て無視
*1)普通の文字か _ の連続
しかし問題もある
一般的な全文検索と比べて処理が足りない
Stemming
Unicode collation algorithm の実装が不完全
そうだPLUGINに行こう
MySQL plugin interface
MySQL 5.0 以前
もともと pluggable だった。
MyISAM, InnoDB などサーバ内部に組み込み済みの storage engine を切り替えられた。
UDF (User Defined Function)は、外部の shared library を組み込むことができる(*2)。
MySQL 5.1 で plugin interface が整備される。
*2)http://dev.mysql.com/doc/refman/5.0/en/create-function-udf.html
CREATE FUNCTION metaphon RETURNS STRING SONAME 'udf_example.so';
MySQL plugin interface
種類(5.1以降)
Storage engineテーブル領域を管理する
Fulltext parser全文検索で使用するパーサを追加する
UDF (User Defined Function)SQL関数を追加する
Daemonmysqld プロセス内でデーモンとして動作するスレッド
Information schemaメタデータ管理用の特殊なデータベースに対する操作を通して呼び出されて動作する。
MySQL plugin interface
種類(6.0以降)
AuditEvent(*1)が処理されるときに呼び出されて動作する
ReplicationMySQLのレプリケーションI/O に同期して処理をする(たぶん)。今現在サンプルなし。
*1) MySQL 6.0 からの新機能。CREATE EVENT でイベントを登録する。
Full-text parser plugin interface
text SQLparser
TABLE FULL-TEXTINDEX
Full-textParserPlugin
(tokenize)
Full-textTextfeed
Full-textTokenfetch
MySQL では全文検索エンジンはこのように構成されている
Pluginはここ
CREATE TABLE hoge (t TEXT,FULLTEXT(t) WITH PARSER geho
);
Full-text parser plugin interface
FTPARSER Plugin がやることは、テキストから検索インデックス用のトークンを抽出することだけ。
Natural language mode, boolean mode などの情報はサーバから渡される
しかし Plugin は MySQL 5.1 以降
MySQL 5.0 の人はどうしているか
Senna / tritonn
全文検索エンジンSenna+そのMySQLへのバインディングtritonn
MySQL 5.0 に対するパッチの形で配布。
SQL 構文を拡張
BOOLEAN MODE の構文は Senna の構文で、MySQL標準仕様の拡張に相当。
CREATE TABLE hoge ( t TEXT, FULLTEXT INDEX USING NGRAM (t) )
mysqlftppc
トークンの抽出方法もいろいろあります
現在以下の plugin があります。
bigram
mecab
space
suffix
Snowball
全ての plugin で Unicode normalization が使えます(デフォルト無効)
mysqlftppc bigram plugin
概要
文字2グラムを使います
MySQL 検索規則
Natural Language Mode は実行できますが、その解釈は難しいです。使いません。使わないでください。
Boolean Mode では原則的に常にフレーズ検索を行います。「東京都」という文字列は「東京」「京都」の二つの連続トークン列(フレーズ)を探すことになります。
mysqlftppc mecab plugin
概要
形態素で検索したい場合
mecab ライブラリとリンクしていいます
日本語の文章を扱うなら、難しいことを言わないのであれば、これがいいです
MySQL 検索規則
Natural Langauge Mode では検索文を形態素解析します
Boolean Mode ではフレーズクエリ指定された文字列は形態素解析します。それ以外は形態素での条件指定と解釈します。
mysqlftppc space plugin
概要
空白文字区切りでトークンを抽出します
デフォルトのパーサよりもさらに機械的で、空白以外は全てトークンに含めます。デフォルトのパーサは漢字などは除去します。
MySQL 検索規則
デフォルトのパーサとほぼ同じと考えて構いません
mysqlftppc suffix plugin
概要
Suffix Array に触発されて作りました。
全ての suffix をトークンとして抽出します
MySQL 検索規則
Natural language mode は実行できますが、解釈は難しいです
Boolean mode では常に先頭一致で検索します。
mysqlftppc snowball plugin
概要
Snowball ライブラリとリンクします
デフォルトのパーサに stemming の前処理を加える
たとえば laughing が含まれる文章を laugh で検索できる
各言語用のパーサに切り替えられる
MySQL 検索規則
デフォルトのパーサと同じと考えて構いません
Installation
fthoge.so というファイルひとつだけ。
configure script
mysql_config を使ってインストール先を決めます。明示的に指定してもかまいません。
prefix は指定しません。指定しても使われません。どうせファイルひとつだけですし。
mysql_config が $PATH に入っていれば ./configure; make; make install 以上終了です。
Installation
MySQL への組み込み。
INSTALL PLUGIN hoge SONAME 'libfthoge.so'
SHOW PLUGIN
全文検索インデックスの作成
CREATE TABLE hoge (t TEXT, FULLTEXT(t) WITH PARSER hoge)
データの投入と検索
INSERT INTO hoge VALUES(“geho”)
SELECT * FROM hoge WHERE MATCH(t) AGAINST(“geho” IN BOOLEAN MODE)
開発経緯
なんとなく検索したい要求が(数だけはたくさん)ありそうだった
検索って何?を聞いてみると
日本語の自然文検索(全文検索)
日付範囲を絞り込みたい
~が~のものを検索したい(属性検索)
よくわからないけどとりあえず「検索」と発言する人達
検索でイメージするものには人によってばらつきがあります。
実装もさまざまあります。
全文検索エンジン
全文検索エンジン
文章(全文)を対象として文字列で条件に適合するかどうかを検査する。Full-text search
Namazuあるファイルがテキストを含んでいるとして、そのテキストを検索可能にする。
検索した結果はファイル一覧。
Rast, HyperEstraierある文書がテキストを含んでいるとして、そのテキストを検索可能にする。
検索した結果は文書ID一覧。
ついでに(全文検索できない)プロパティを追加管理できる
たとえば電子メール
本文に~~が含まれているものを検索したい
受信日時が~~のものを検索したい
送信者が~~のものを検索したい
件名が~~のものを検索したい
件名に~~が含まれているものを検索したい
…あれ?
全文検索エンジンから
テキスト=
ファイル
プロパティ
プロパティ
プロパティ
プロパティ
検索エンジンへ
何か(検索単位)
プロパティ
受信日時
件名
本文
こういった管理体系を実現するには…?
検索エンジン
たとえば Lucene
検索対象は属性(プロパティ)を持っている。
その属性は全文検索の対象となるテキストかもしれない。
そうじゃないかもしれない。数値かもしれないし、日付かもしれない。
検索エンジン
Solr / Lucene
Lucene は検索エンジンを管理する Java ライブラリ
通常あるディレクトリ以下にインデックスファイルを構築する
ファイル形式は独自形式
設定しだいで Analyzer を差し替え可能
Solr は Lucene を Web 経由で呼び出しできるようにするサーブレット
Lucene 自体はレプリケーションやマルチマスタなどの機能は持っていないので、Solr 側で制御したりする
たとえば Tomcat 上に乗せる
通信プロトコルは多彩
PHP native, POST, XML ベースの通信など
検索エンジン
HyperEstraier
2gram で分解して、転置インデックスを作る
扱えるテキスト(全文検索)は一つ。そのほかに属性が管理できる。
Estmaster という Web サーバデーモンが付属している
C で実装されている
ノード API で分散処理することもできる
文字列に対して独自の正規化が行われる
属性検索
属性検索は意外と大変。
数値比較
日付比較
カレンダーを引く計算が必要(Timezone)。
Timestamp は不十分。負数や小数の問題。
例)生年月日
文字列比較
~で始まる、~で終わる、辞書順(COLLATION)
実は属性検索のニーズが多い
Windows NTFS のファイル管理を思い浮かべてください。
RDBMS
MySQL
検索単位は行
検索対象は属性(カラム)持っている。
SQL で検索する
型に応じた検索条件が指定できる
SELECT * FROM hoge WHERE geho1=”foo”AND geho2 < 4AND YEAR(geho3) = '2009'
検索エンジン vs RDBMS vs XMLDB
属性の追加(スキーマ)
検索エンジンは多くの場合属性は動的に追加できる
RDBMS は CREATE TABLE でスキーマ定義が必須
RDBMSに対するスキーマレスといえばXMLDB
否定で検索するか~?
検索エンジンは「~がない」で検索できない。しない。
RDBMSではNULL条件指定に相当するが…効率悪い
属性が構造体だったらどうする?
そのまま行に押し込むにはXMLDB(XQuery)
RDBMS は正規化の過程で別テーブルに変化する
キーワードとプロダクト
全文検索エンジン
検索エンジン
属性検索
RDBMS
MySQL
XML
XMLDB
key/valuedatabase
LuceneBerkeley DB
memcached
Tokyo Cabinet
Hyper Estraier
Oracle
Namazu
そんなこんなで mysqlftppc
MySQL を使いたかった
運用実績(手順)
既にたくさんの MySQL を運用している
開発環境
ライブラリやプロトコル
データの信頼性
Recovery
Replication
Backup
Senna でもいいけど、パッチ管理は面倒
せっかくだから 5.1 plugin ⇒作るしかFTPARSER
mysqlftppc
なんでこんなにたくさんの種類があるのか
「全文検索」というものの性質
全文検索
一般的なケース
保存対象は自然言語の文章
検索も自然言語
ある検索条件から得られる結果が「期待」するものであるためには
「期待」はケース・バイ・ケースです。ほとんど気持ちの問題。
一般的な解というものはありません。
さまざまな戦略が考えられます。
Tokenize
戦略の一つとして、文章の文字列を一度「トークン」という単位に分解することからはじめる実装は多いです。
MySQL もこの戦略をとります。
Tokenize
「適切なトークン」は言語の特性に依存
まず文字で表現されていることが前提。
Unicodeでは文字の正規化に関する仕様があり、要求と合致すれば大変便利。Unicode normalization
日本語の全角半角カナの表記ゆれは、このレベルで吸収することもできる。
英語などはスペースで区切れば、大体オーケー。
一部特殊なケースもある(ハイフンでつながっているなど)
検索時のためにもう少し処理しないといけない場合もある
現在日本語に対してトークンを得る手法で実用的なものは、大雑把に 2 種類
形態素
文字N-gram
形態素
形態素解析の前提知識
まず「形態素」というものがあると仮定するのが前提
そうではないという考え方を否定するわけではない。
日本語では日本語の品詞で分類された形態素を使うのが一般的
品詞体系は一つじゃない。
実用上使いやすいであろうという基準で整備されてきたものの一つに naist-jdic がある。
日本語の文章は品詞体系に沿って形態素に分解
Mecab
確立を使って尤もらしい分解を行う
「辞書」に形態素の知識や確立に関する知識を持つ
文字n-gram
文字 n-gram の前提知識
文章は文字の羅列で表現されている
文字の羅列をうまく検索できるといい
文字を単位として、とにかく分解しよう
文字 2 グラム
「本日は晴天なり」
「本日」「日は」「は晴」「晴天」「天な」「なり」
形態素解析か文字ngramか
Mecab
辞書の使っている品詞体系と自分の思惑とを合致させておくことが重要
検索漏れ?のように錯覚してしまうこともある
確立で処理しているので、間違うこともある
解析したい日本語と、辞書の学習した日本語の性質が異なっているかもしれない
普通に検索漏れとして見える
文字ngram
とにかく文字列に合致したものが全て見つかる
「それじゃない」と感情的に思えてしまうことも多々ある
「京都」で検索すると「東京都」が見つかる
mysqlftppc
なんでこんなにたくさんの種類があるのか
「全文検索」というものの性質
日本語
形態素かngramか:両方用意したので、使い分けてください
キーワードで検索
「キーワード」で検索したい場合。
「キーワードの羅列を保存して検索できるようにする」という設計もアリ。全文検索。
キーワードを空白区切りで連結する。
キーワードは空白を含むかもしれない。→エスケープシーケンスが必要。
組み込みパーサでは無理。
mysqlftppc space plugin を使う
Senna / tritonn では delimited に相当。
mysqlftppc
なんでこんなにたくさんの種類があるのか
「全文検索」というものの性質
日本語
形態素かngramか:両方用意したので、使い分けてください(bigram, mecab)
キーワード
space plugin 使ってください
stemming
活用形を含めて検索したいケースがある
He lives in Tokyo.
I live in Tokyo.
“live in Tokyo” で検索し、両方見つけたい。
Snowball という良いライブラリがある
多言語対応(欧米)
mysqlftppc
なんでこんなにたくさんの種類があるのか
「全文検索」というものの性質
日本語
形態素かngramか:両方用意したので、使い分けてください(bigram, mecab)
キーワード
space plugin 使ってください
Stemming
Snowball plugin 使ってください
full-text parser
Built-in parser Plugin parser
engine
text
parser->parse()
param->mysql_add_word()
param->mysql_parse()
* built-in parser
ft_min_word_lenft_max_word_lenft_stopword_file
full-text parser
Built-in parser Plugin parser
engine
text
parser->parse()
param->mysql_add_word()
param->mysql_parse()
* bigram parser* mecab parser* space parser* suffix parser
ft_min_word_lenft_max_word_lenft_stopword_file
full-text parser
Built-in parser Plugin parser
engine
text
parser->parse()
param->mysql_add_word()
param->mysql_parse()
* snowball parser
ft_min_word_lenft_max_word_lenft_stopword_file
解決できていない問題
スコアリング
ならび順の制御
おそらく UDF で制御することになる(効率の問題)
Natural language mode
半分以上ヒットしたら、そのキー情報は使わない
Query expansion
独自カラーは結構強い
内部でどんな処理が起こっているのか伺いにくい
次期バージョンで UDF 追加予定
MyISAM しかまだ対応してない
以上
Questions?