デフォルトの場合や 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.08 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 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.01 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)
デフォルトでは、大文字と小文字が区別される方法で検索が実行されます。 大/小文字を区別する全文検索を実行するには、インデックス付けされたカラムに大/小文字を区別する照合またはバイナリ照合を使用します。 たとえば、の utf8mb4
文字セットを使用するカラムには、utf8mb4_0900_as_cs
または utf8mb4_bin
の照合順序を割り当てて、全文検索で大文字と小文字を区別できます。
以前に例で示したように、MATCH()
が WHERE
句で使用されると、もっとも関連性の高い行が 1 番目に返されるように、自動的にソートされます。 関連性の値は、負ではない浮動小数点数です。 ゼロの関連性は、類似性がないという意味です。 関連性は、行 (ドキュメント) 内の単語の数、行内の一意の単語の数、コレクション内の単語の合計数および特定の単語を含む行の数に基づいて計算されます。
「document」 という用語は、「row」 という用語と同じ意味で使用でき、どちらの用語も行のインデックス付けされた部分を指します。 「collection」 という用語は、インデックス付けされたカラムを指し、すべての行を含みます。
単に一致をカウントするには、次のようなクエリーを使用してください。
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.10.2「ブール全文検索」およびセクション12.10.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)
二重引用符 ("
) 文字内で囲まれたフレーズは、入力されたそのままのフレーズを含む行にのみ一致します。 全文エンジンでは、フレーズが複数の単語に分割され、それらの単語の FULLTEXT
インデックス内で検索が実行されます。 単語以外の文字は、正確に一致する必要がありません。フレーズ検索では、そのフレーズとまったく同じ単語が同じ順序で一致に含まれることのみが必要です。 たとえば、"test phrase"
は "test, phrase"
と一致します。 フレーズにインデックス内にある単語が含まれない場合は、結果が空になります。 たとえば、すべての単語がストップワードであるか、インデックス付けされた単語の最小長より短い場合、結果は空になります。
MySQL FULLTEXT
の実装では、トゥルーワード文字 (文字、数字、およびアンダースコア) のシーケンスが単語とみなされます。 そのシーケンスには、アポストロフィー ('
) も含めることはできますが、1 行に 1 つまでです。 つまり、aaa'bbb
は 1 語とみなされますが、aaa''bbb
は 2 語とみなされます。 単語の先頭または末尾のアポストロフィーは、FULLTEXT
パーサーによって削除されます。'aaa'bbb'
は、aaa'bbb
として解析されます。
組込み FULLTEXT
パーサーは、
(空白)、,
(カンマ)、.
(ピリオド) などの特定のデリミタ文字を検索して、単語の開始位置と終了位置を決定します。 単語がデリミタで区切られていない場合 (中国語など)、組込み FULLTEXT
パーサーは単語の開始位置または終了位置を判別できません。 組込み FULLTEXT
パーサーを使用する FULLTEXT
インデックスに、このような言語の単語またはインデックス付けされた他の用語を追加できるようにするには、それらが任意のデリミタで区切られるように事前処理する必要があります。 または、ngram パーサープラグイン (中国語、日本語または韓国語) または MeCab パーサープラグイン (日本語) を使用して、FULLTEXT
インデックスを作成できます。
組み込みの全文パーサーを置き換えるプラグインを記述できます。 詳細は、The MySQL Plugin APIを参照してください。 パーサープラグインのサンプルソースコードについては、MySQL のソース配布の plugin/fulltext
ディレクトリを参照してください。
全文検索では、一部の単語が無視されます。
-
短すぎる単語は無視されます。 全文検索で見つかった単語のデフォルトの最小長は、
InnoDB
検索インデックスの場合は 3 文字、MyISAM
の場合は 4 文字です。 インデックスを作成する前に、構成オプション (InnoDB
検索インデックスの場合はinnodb_ft_min_token_size
構成オプション、MyISAM
の場合はft_min_word_len
) を設定すると、カットオフを制御できます。注記この動作は、ngram パーサーを使用する
FULLTEXT
インデックスには適用されません。 ngram パーサーの場合、トークンの長さはngram_token_size
オプションによって定義されます。 ストップワードリスト内の単語は無視されます。 ストップワードは、セマンティクス値がゼロであると考えられるほど一般的な単語 (「the」 や 「some」 など) です。 組み込みのストップワードリストもありますが、ユーザー定義のリストでオーバーライドできます。 ストップワードリストおよび関連する構成オプションは、
InnoDB
検索インデックスとMyISAM
検索インデックスとで異なります。 ストップワードの処理は、InnoDB
検索インデックスの場合は構成オプションinnodb_ft_enable_stopword
、innodb_ft_server_stopword_table
、およびinnodb_ft_user_stopword_table
、MyISAM
検索インデックスの場合はft_stopword_file
によって制御されます。
デフォルトのストップワードリストおよびそれを変更する方法を表示する方法については、セクション12.10.4「全文ストップワード」を参照してください。 単語のデフォルト最小長は、セクション12.10.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.10.2「ブール全文検索」で説明したブール検索モードを使用したりできます。