MATCH (col1
,col2
,...) AGAINST (expr
[search_modifier
])search_modifier:
{ IN BOOLEAN MODE | IN NATURAL LANGUAGE MODE | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION | WITH QUERY EXPANSION }
MySQL は全文インデックスおよび検索をサポートします :
MySQL の全文インデックスは、タイプ
FULLTEXT
のインデックスです。
全文インデックスは MyISAM
テーブルとのみ使用されており、CHAR
、VARCHAR
、または
TEXT
カラムのためにだけ作成されます。
FULLTEXT
インデックスの定義は、テーブルを作成する時に、CREATE
TABLE
文で提示することができるほか、ALTER
TABLE
または CREATE INDEX
を使用して後で付け加えることも可能です。
大きなデータセットに関しては、FULLTEXT
インデックスを持たないテーブルにロードし、その後でインデックスを作成するほうが、すでに
FULLTEXT
インデックスを持つテーブルにロードするよりも断然速く読み込めます。
全文検索は MATCH() ... AGAINST
シンタックスを用いて行われます。MATCH()
は、検索用にカラムに名称をつける、カンマで区切られたリストを使用します。AGAINST
は検索するストリングと、実行する検索のタイプを示すオプションの修飾子を利用します。検索ストリングは、変数やカラム名ではなく、リテラル
ストリングでなければなりません。全文検索には3種類あります
:
ブール検索は、特別なクエリ言語のルールを使用した検索ストリングを解釈します。ストリングは検索の対象になる言葉を含みます。また、単語は整合行で提示または不提示にされなければならない、もしくは、通常より高く、または低く加重するべき、等の条件を指定する演算子も含むことができます。「some」
や 「then」
のような一般的な単語はストップワードで、検索ストリングにあってもマッチしません。IN
BOOLEAN MODE
修飾子はブール検索を特定します。詳細は
項11.7.1. 「ブール全文検索」 をご覧ください。
自然言語の検索は、検索ストリングを人間の自然な言語でのフレーズ
( フリーテキストのフレーズ )
として解釈します。これには特別な演算子はありません。ストップワード
リストは適用されます。また、行の 50%
以上にある言葉は常用語と判断され、検出はされません。全文検索は、IN
NATURAL LANGUAGE MODE
修飾子が与えられている、または修飾子がまったくない場合は、自然言語検索になります。
クエリ拡張検索は、自然言語検索が改変されたものです。自然言語検索を行うには、検索ストリングが使用されます。そして、検索によって返された最も関連性の強い行からの言葉が検索ストリングに加えられ、再度検索されます。クエリは
2 度目の検索からの行を戻します。IN
NATURAL LANGUAGE MODE WITH QUERY EXPANSION
または
WITH QUERY EXPANSION
修飾子は、クエリ拡張検索を特定します。詳細は
項11.7.2. 「クエリ拡張を伴う全文検索」
をご覧ください。
IN NATURAL LANGUAGE MODE
および IN
NATURAL LANGUAGE MODE WITH QUERY EXPANSION
修飾子は、MySQL 5.1.7 から追加されました。
全文検索の制約は、項11.7.4. 「全文制限」 に挙げられています。
mysql>CREATE TABLE articles (
->id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
->title VARCHAR(200),
->body TEXT,
->FULLTEXT (title,body)
->);
Query OK, 0 rows affected (0.00 sec) mysql>INSERT INTO articles (title,body) VALUES
->('MySQL Tutorial','DBMS stands for DataBase ...'),
->('How To Use MySQL Well','After you went through a ...'),
->('Optimizing MySQL','In this tutorial we will show ...'),
->('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
->('MySQL vs. YourSQL','In the following database comparison ...'),
->('MySQL Security','When configured properly, MySQL ...');
Query OK, 6 rows affected (0.00 sec) Records: 6 Duplicates: 0 Warnings: 0 mysql>SELECT * FROM articles
->WHERE MATCH (title,body)
->AGAINST ('database' IN NATURAL LANGUAGE MODE);
+----+-------------------+------------------------------------------+ | id | title | body | +----+-------------------+------------------------------------------+ | 5 | MySQL vs. YourSQL | In the following database comparison ... | | 1 | MySQL Tutorial | DBMS stands for DataBase ... | +----+-------------------+------------------------------------------+ 2 rows in set (0.00 sec)
MATCH()
関数は、テキスト
コレクション
に対するストリングを自然言語検索します。コレクションは、FULLTEXT
インデックスを含む、ひとつ以上のカラムのセットです。検索ストリングは、AGAINST()
への引数として与えられます。テーブルの各行に対し、検索ストリングと、MATCH()
リストで名付けられたカラムの行内のテキスト間の類似性を測り、MATCH()
が関連性のある値を戻します。
デフォルトでは、検索は大文字小文字の区別のある方法で行われます。しかし、バイナリ照合を用いて、インデックスのつけられたカラムに対し、大文字小文字の区別のある古テキスト検索を行うことができます。例えば、latin1
文字セットを使用するカラムに、全文検索のために大文字小文字の区別をするよう、latin1_bin
の照合を割り当てることができます。
以前に挙げた例のように、MATCH()
が
WHERE
句で使用されるとき、返された行はまず、最高レベルの関連性があるとした上で自動的に保管されます。関連値は、負でない浮動小数点数です。ゼロ
レリバンスとは、類似性がまったくないという意味です。レリバンスは、行の単語の数、行の一意性のある単語の数、コレクション内の単語の合計数、そして特定の単語を含む資料
( 行 ) の数に基づいて計算されます。
単に検出を数えるには、次のクエリを使用してください :
mysql>SELECT COUNT(*) FROM articles
->WHERE MATCH (title,body)
->AGAINST ('database' IN NATURAL LANGUAGE MODE);
+----------+ | COUNT(*) | +----------+ | 2 | +----------+ 1 row in set (0.00 sec)
しかし、次のようにクエリを書き換えたほうが手軽な場合もあります :
mysql>SELECT
->COUNT(IF(MATCH (title,body) AGAINST ('database' IN NATURAL LANGUAGE MODE), 1, NULL))
->AS count
->FROM articles;
+-------+ | count | +-------+ | 2 | +-------+ 1 row in set (0.03 sec)
最初のクエリは関連性の大きさによって結果をソートし、2 番目のクエリではそれを行いません。しかし、2 番目のクエリは 1 番目が行わない、テーブル全体のスキャンをします。1 番目は数行のマッチしか検出されなければ時間はかかりませんが、そうでなくとも 2 番目は、どちらにしても多くの行を読むので素早く終わります。
自然言語の全文検索では、MATCH()
関数で名付けられたカラムが、テーブルの
FULLTEXT
インデックスのどれかに含まれるカラムと同じでなければなりません。先行のクエリに関しては、MATCH()
関数で名付けられたカラム ( title
and body
) は、article
テーブルの FULLTEXT
インデックスの定義で名付けられたものと同じです。title
と body
を別々に検索したい場合は、各カラムに別々の
FULLTEXT
インデックスを作成する必要があります。
また、ブール検索もしくはクエリ拡張との検索を行うことも可能です。これらの検索タイプは 項11.7.1. 「ブール全文検索」 と 項11.7.2. 「クエリ拡張を伴う全文検索」 で説明されています。
インデックスを用いた全文検索は、インデックスが複数のテーブルをまたぐことはできないため、MATCH()
句の単一テーブルからのカラムにしか名前が付けられません。ブール検索はインデックスがなくても行えます
( ただしスピードは落ちる )
。その場合、複数のテーブルからのカラムを名付けることは可能です。
先行の例は、関連性が減少する順序に行が戻される
MATCH()
関数の使い方を簡単に説明したものでした。次の例は関連値を明示的に引き出す方法です。SELECT
が WHERE
句も ORDER BY
句も含んでいないため、行は順序付けられていません
:
mysql>SELECT id, MATCH (title,body)
->AGAINST ('Tutorial' IN NATURAL LANGUAGE MODE) AS score
->FROM articles;
+----+------------------+ | id | score | +----+------------------+ | 1 | 0.65545833110809 | | 2 | 0 | | 3 | 0.66266459226608 | | 4 | 0 | | 5 | 0 | | 6 | 0 | +----+------------------+ 6 rows in set (0.00 sec)
次の例はさらに複雑なものです。クエリは関連値を戻し、また、関連性が減少する順序に行をソートします。この結果を得るため、MATCH()
を 2 度指定してください : 一度は
SELECT
リスト、そしてもう一度は
WHERE
句で指定します。MySQL
の最適化プログラムが、ふたつの
MATCH()
呼び出しがまったく同じもので、全文検索コードを一度だけ実行されることに気付くため、これによって追加のオーバーヘッドが起こることはありません。
mysql>SELECT id, body, MATCH (title,body) AGAINST
->('Security implications of running MySQL as root'
->IN NATURAL LANGUAGE MODE) AS score
->FROM articles WHERE MATCH (title,body) AGAINST
->('Security implications of running MySQL as root'
->IN NATURAL LANGUAGE MODE);
+----+-------------------------------------+-----------------+ | id | body | score | +----+-------------------------------------+-----------------+ | 4 | 1. Never run mysqld as root. 2. ... | 1.5219271183014 | | 6 | When configured properly, MySQL ... | 1.3114095926285 | +----+-------------------------------------+-----------------+ 2 rows in set (0.00 sec)
MySQL FULLTEXT
の実装は、true 語文字
(文字、数字、および線文字 )
のすべてのシークエンスを言葉みなします。そのシークエンスはまた、アポストロフィ
( ‘'
’ )
も含むことができますが、1
行にひとつのみです。つまり、aaa'bbb
は一語とみなされますが、aaa''bbb
は二語の扱いです。単語の頭または終わりのアポストロフィは、FULLTEXT
パーサが取ってしまうので、'aaa'bbb'
ならば aaa'bbb
になります。
FULLTEXT
パーサは特定の区切り文字を見て、語の頭と最後を定義します。その例には、‘
’
( スペース ) , ‘,
’ ( カンマ
) , そして ‘.
’ ( ピリオド )
があります。単語が非区切り文字 ( 例えば中国語
) で区切られている場合は、FULLTEXT
パーサは単語の最初と最後を定義することができません。単語や、インデックスのついた他の表現をそのような言語で
FULLTEXT
インデックスに加えるには、事前に処理して
‘"
’
などの任意の区切り文字で区切る必要があります。
MySQL 5.1
では、組み込まれた全文パーサを置き換えるプラグインを書くことができます。詳細は、項25.2. 「The MySQL Plugin Interface」
を参照してください。例えば、パーサ
プラグンのソースコードについては、MySQL
のソース配布物の plugin/fulltext
ディレクトリをご覧ください。
単語のあるものは、全文検索では無視されます :
短すぎる単語は無視されます。全文検索で検出される言葉で最も短いものは 4 文字です。
ストップワード リストにある言葉は無視されます。ストップワードは 「the」 や 「some」 などの常用語で、語義の値はゼロとされています。すでに組み込まれているストップワードのリストがありますが、ユーザ定義リストで書き換えることができます。
デフォルトのストップワード リストは 項11.7.3. 「全文ストップワード」 で挙げられています。デフォルトの最短の単語の長さとストップワード リストは、項11.7.5. 「微調整 MySQL 全文検索」 で説明されているように変更することができます。
コレクションとクエリの中のすべての正しい言葉は、コレクションまたはクエリでの重要性によって加重されています。従って、多くの資料に登場する言葉は、このコレクションでは語義の値が低いので、比重が低く ( あるものはゼロ ) なっています。逆に、稀な言葉は高く重みづけがされます。言葉の比重は、行の関連性を計算するために組み合わせて応用されます。
このような技術は、コレクションが大きいほど効果的に作用します
( 実際、そうなるように綿密に調整されています
)
。ごく小さなテーブルでは、言葉の分配が語義の値を適切に反映しないため、この形式においては時に不可解な結果が出ることがあります。例えば、「MySQL」
という言葉は既出の articles
テーブルのすべての行に含まれていますが、この単語で検索しても結果は出ません
:
mysql>SELECT * FROM articles
->WHERE MATCH (title,body)
->AGAINST ('MySQL' IN NATURAL LANGUAGE MODE);
Empty set (0.00 sec)
「MySQL」 という言葉は少なくとも 50 % の行で提示されているため、検索結果は空になります。このように、この言葉は効果的にストップワードとして扱われます。大きなデータセットでは、これは最も望ましい動作です : 自然言語のクエリは、1GB テーブルの毎 2 行目は戻さないようになっています。小さなデータセットにとっては、これはあまり望ましい動作ではありません。
テーブルの行の半分にマッチする言葉は、関連のある資料を見つけるのに適しません。事実、関連のないものも大量に検出されるでしょう。これはインターネットのサーチエンジンでの検索と同じ論理です。このため、この言葉を含む行は、この特定のデータセットにおいて 語義の値が低く定められています。あるデータセットでは、提示された単語が 50% の境界値を越えても、他のデータセットではまた異なります。
50% の境界値は、全文検索を行うとその重要性が明らかになります : テーブルを作成し、テキストの 1 行または 2 行のみをインサートしてみると、テキストのすべての単語は少なくとも 50% の行に存在することが分かります。そのため、検出結果は検出されません。少なくとも 3 行以上をインサートするようにしてください。50% の制限を避ける必要がある場合は、ブール検索をお試しください。詳細は 項11.7.1. 「ブール全文検索」 をご覧ください。