デフォルトの場合や IN NATURAL LANGUAGE
MODE
修飾子が指定された場合は、MATCH()
関数は、テキストコレクションに対して文字列の自然言語検索を実行します。コレクションは、FULLTEXT
インデックスに含まれる 1
つ以上のカラムのセットです。検索文字列は、AGAINST()
への引数として指定されます。MATCH()
は、テーブルの行ごとに関連性の値を返します。つまり、検索文字列と、MATCH()
リストで名前が指定されたカラムの該当行のテキスト間で類似性が評価されます。
mysql> CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body)
) ENGINE=InnoDB;
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 |
+----+-------------------+------------------------------------------+
| 1 | MySQL Tutorial | DBMS stands for DataBase ... |
| 5 | MySQL vs. YourSQL | In the following database comparison ... |
+----+-------------------+------------------------------------------+
2 rows in set (0.00 sec)
デフォルトでは、大文字と小文字が区別される方法で検索が実行されます。大文字と大文字が区別された全文検索を実行するには、インデックス付きカラムのバイナリ照合順序を使用します。たとえば、全文検索で大文字と小文字が区別されるように、latin1
文字セットが使用されているカラムに
latin1_bin
の照合順序を割り当てることができます。
以前に例で示したように、MATCH()
が WHERE
句で使用されると、もっとも関連性の高い行が
1
番目に返されるように、自動的にソートされます。関連性の値は、負ではない浮動小数点数です。ゼロの関連性は、類似性がないという意味です。関連性は、行内の単語の数、該当行内の一意な単語の数、コレクション内の単語の合計数、および特定の単語を含むドキュメント
(行) の数に基づいて計算されます。
単に一致をカウントするには、次のようなクエリーを使用してください。
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)
1 つ目のクエリーでは、いくつかの追加作業
(関連性別の結果のソート)
が実行されますが、WHERE
句に基づくインデックス検索を使用することもできます。インデックス検索では、検索でほとんど行が一致しない場合に、最初のクエリーが速くなる可能性があります。2
つ目のクエリーではテーブルの完全スキャンが実行され、ほとんどの行に検索語句が存在した場合に、インデックス検索よりも高速になる可能性があります。
自然言語による全文検索では、MATCH()
関数で名前が指定されたカラムは、テーブル内の一部の
FULLTEXT
インデックスに含まれるカラムと同じである必要があります。上記のクエリーでは、MATCH()
関数で名前が指定されたカラム
(title
と body
)
は、article
テーブルの
FULLTEXT
インデックスの定義で名前が指定されたカラムと同じです。title
または body
を個別に検索するには、カラムごとに個別の
FULLTEXT
インデックスを作成します。
ブール検索やクエリー拡張を使用した検索を実行することもできます。これらの検索タイプについては、セクション12.9.2「ブール全文検索」およびセクション12.9.3「クエリー拡張を使用した全文検索」で説明されています。
インデックスを使用した全文検索では、インデックスが複数のテーブルに及ぶ可能性はないため、MATCH()
句の単一テーブルにあるカラムにしか名前を付けることができません。MyISAM
テーブルでは、インデックスが存在しない場合でも、ブール検索を実行できます
(ただし、低速になります)。この場合、複数のテーブルのカラムに名前を指定できます。
上記の例では、関連性の降順で行が返される
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.22764469683170319 |
| 2 | 0 |
| 3 | 0.22764469683170319 |
| 4 | 0 |
| 5 | 0 |
| 6 | 0 |
+----+---------------------+
6 rows in set (0.00 sec)
次の例はさらに複雑です。このクエリーでは関連性の値が返され、関連性の降順での行のソートも行われます。この結果を実現するには、MATCH()
を 2 回 (1 回は SELECT
リストに、もう 1 回は WHERE
句に)
指定します。MySQL オプティマイザによって、2
回の MATCH()
呼び出しが同じであり、全文検索コードが 1
回のみ起動されることが検出されるため、追加のオーバーヘッドは発生しません。
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
の実装では、トゥルーワード文字
(文字、数字、およびアンダースコア)
のシーケンスが単語とみなされます。そのシーケンスには、アポストロフィー
(「'
」)
も含めることはできますが、1 行に 1
つまでです。つまり、aaa'bbb
は 1
語とみなされますが、aaa''bbb
は 2
語とみなされます。単語の先頭または末尾のアポストロフィーは、FULLTEXT
パーサーによって削除されます。'aaa'bbb'
は、aaa'bbb
として解析されます。
FULLTEXT
パーサーは、特定の区切り文字 (「
」
(空白)、「,
」
(カンマ)、「.
」 (ピリオド)
など)
を検索することで、単語の先頭と末尾を特定します。単語が区切り文字で区切られていない場合
(たとえば中国語の場合)、FULLTEXT
パーサーでは単語の先頭と末尾を特定できません。このような言語で単語やその他のインデックス付きの語句を
FULLTEXT
インデックスに追加するには、「"
」
などの任意の区切り文字で区切られるように前処理を行う必要があります。
MySQL 5.6
では、組み込みの全文パーサーから置き換えられるプラグインを作成できます
(MyISAM
でのみサポートされています)。詳細は、セクション24.2「MySQL プラグイン API」を参照してください。パーサープラグインのサンプルソースコードについては、MySQL
のソース配布の plugin/fulltext
ディレクトリを参照してください。
全文検索では、一部の単語が無視されます。
短すぎる単語は無視されます。全文検索で見つかった単語のデフォルトの最小長は、
InnoDB
検索インデックスの場合は 3 文字、MyISAM
の場合は 4 文字です。インデックスを作成する前に、構成オプション (InnoDB
検索インデックスの場合はinnodb_ft_min_token_size
構成オプション、MyISAM
の場合はft_min_word_len
) を設定すると、カットオフを制御できます。ストップワードリスト内の単語は無視されます。ストップワードは、セマンティクス値がゼロであると考えられるほど一般的な単語 (「the」 や 「some」 など) です。組み込みのストップワードリストもありますが、ユーザー定義のリストでオーバーライドできます。ストップワードリストおよび関連する構成オプションは、
InnoDB
検索インデックスとMyISAM
検索インデックスとで異なります。ストップワードの処理は、InnoDB
検索インデックスの場合は構成オプションinnodb_ft_enable_stopword
、innodb_ft_server_stopword_table
、およびinnodb_ft_user_stopword_table
、MyISAM
検索インデックスの場合はft_stopword_file
によって制御されます。
デフォルトのストップワードリストおよびそれを変更する方法を表示する方法については、セクション12.9.4「全文ストップワード」を参照してください。単語のデフォルト最小長は、セクション12.9.6「MySQL の全文検索の微調整」で説明したように変更できます。
コレクションおよびクエリー内のすべての正確な単語は、コレクションまたはクエリーでの重要性に従って重み付けられます。したがって、多くのドキュメント内に存在する単語では、この特定のコレクション内のセマンティクス値が低くなるため、重みも低くなります。反対に、まれな単語には、高い重みが付けられます。単語の重みを組み合わせることで、行の関連性が計算されます。この技術は、大きなコレクションで最適に機能します。
非常に小さなテーブルでは、単語の配布が適切にセマンティクス値に反映されないため、このモデルでは、MyISAM
テーブル上の検索インデックスに対して異常な結果が生成される可能性があります。たとえば、以前に示した
articles
テーブルのすべての行には、「MySQL」
という単語が存在しますが、MyISAM
検索インデックス内の単語を検索しても結果が生成されません。
mysql> SELECT * FROM articles
WHERE MATCH (title,body)
AGAINST ('MySQL' IN NATURAL LANGUAGE MODE);
Empty set (0.00 sec)
「MySQL」 という単語は 50% 以上の行に存在するため、検索の結果が空になります。そのため、事実上ストップワードとして処理されます。このフィルタ処理技術は、よくある語句に対して不適切な結果が生成される可能性のある小さなデータセットよりも、1G バイトのテーブルから 1 行おきに結果セットが返される可能性のない大きなデータセットに適しています。
全文検索がどのように動作するかを確認するために最初に試すと、しきい値が
50%
であることに驚くかもしれませんが、これによって
InnoDB
テーブルが全文検索での実験によりふさわしいことがわかります。MyISAM
テーブルを作成し、それに 1、2
行のテキストのみを挿入する場合は、テキスト内のすべての単語が
50%
以上の行に出現します。その結果、テーブルにより多くの行が含まれるまで、検索の結果が返されません。50%
の制限を回避する必要のあるユーザーは、InnoDB
テーブル上に検索インデックスを構築したり、セクション12.9.2「ブール全文検索」で説明したブール検索モードを使用したりできます。