Documentation Home
MySQL 5.6 リファレンスマニュアル
Download this Manual
PDF (US Ltr) - 27.1Mb
PDF (A4) - 27.2Mb
HTML Download (TGZ) - 7.2Mb
HTML Download (Zip) - 7.2Mb


12.9.1 自然言語全文検索

デフォルトの場合や 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() 関数で名前が指定されたカラム (titlebody) は、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) を設定すると、カットオフを制御できます。

  • ストップワードリスト内の単語は無視されます。ストップワードは、セマンティクス値がゼロであると考えられるほど一般的な単語 (thesome など) です。組み込みのストップワードリストもありますが、ユーザー定義のリストでオーバーライドできます。ストップワードリストおよび関連する構成オプションは、InnoDB 検索インデックスと MyISAM 検索インデックスとで異なります。ストップワードの処理は、InnoDB 検索インデックスの場合は構成オプション innodb_ft_enable_stopwordinnodb_ft_server_stopword_table、および innodb_ft_user_stopword_tableMyISAM 検索インデックスの場合は ft_stopword_file によって制御されます。

デフォルトのストップワードリストおよびそれを変更する方法を表示する方法については、セクション12.9.4「全文ストップワード」を参照してください。単語のデフォルト最小長は、セクション12.9.6「MySQL の全文検索の微調整」で説明したように変更できます。

コレクションおよびクエリー内のすべての正確な単語は、コレクションまたはクエリーでの重要性に従って重み付けられます。したがって、多くのドキュメント内に存在する単語では、この特定のコレクション内のセマンティクス値が低くなるため、重みも低くなります。反対に、まれな単語には、高い重みが付けられます。単語の重みを組み合わせることで、行の関連性が計算されます。この技術は、大きなコレクションで最適に機能します。

MyISAM の制限

非常に小さなテーブルでは、単語の配布が適切にセマンティクス値に反映されないため、このモデルでは、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「ブール全文検索」で説明したブール検索モードを使用したりできます。


User Comments
  Posted by Roland van der Heijden on May 14, 2009
I totally agree. It took me even longer to find this (although i discovered some interesting commands, like myisam_ftdump, along the way).

Make this VERY CLEAR at the top of the FTS pages.
  Posted by Wagner Bianchi on August 15, 2011
See how works the FULLTEXT SEARCH when the search term is at least 50% on the rows of a table:

mysql> CREATE TABLE sgbds (
-> sgbd char(60) not null,
-> FULLTEXT(sgbd)
-> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.05 sec)

mysql> INSERT INTO sgbds SET sgbd ='MySQL';
Query OK, 1 row affected (0.00 sec)

mysql> SELECT sgbd FROM sgbds WHERE MATCH(sgbd) AGAINST('MySQL');
Empty set (0.03 sec)

mysql> INSERT INTO sgbds SET sgbd ='ORACLE';
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO sgbds SET sgbd ='SQL Server';
Query OK, 1 row affected (0.00 sec)

mysql> SELECT sgbd FROM sgbds WHERE MATCH(sgbd) AGAINST('MySQL');
+-------+
| sgbd |
+-------+
| MySQL |
+-------+
1 row in set (0.00 sec)

When you INSERT the first value, it is a hundred percent of a table. After INSERT more two values ... "tachan nan nan", that value "MySQL" now is the 33% of table rows and it is returned.

Happy MySQL'ing 4 all!
  Posted by Arek Jaworski on December 11, 2009
I completely agree with Casey and Roland.
Today, I was checking why search is not working with one of the components in Joomla. Turned on debugging mode. Checked SQL queries. Everything looked fine. Search the Internet for 'MATCH AGAINST returns zero rows'. No explicit solution found. Did a lot of testing. Finally, read through this doc till the end and... voila! Found what was wrong. I had only two rows inserted. This kind of information ("Be sure to insert at least three rows") should be highlighted, bolded, put in attention box and moved to the very top. It could save a lot of people's time!

  Posted by John Craig on December 16, 2009
I too have run into the no-results problem. In my case, we have plenty rows as a data-set, but the search term ("bankruptcy") simply occurred too many times and was considered a stop-word. While this IS the expected default behavior of MySQL FULLTEXT search, it is not acceptable when the website is all about bankruptcy. (Can you imagine a visitor receiving the message, "no results for 'bankruptcy' at a bankruptcy website?)

The documentation suggests using the BOOLEAN mode which DOES ignore the threshold, but returns NO relevance with the results; A very undesirable behavior.

My current solution, is to use QUERY EXPANSION mode in the SELECT statement, and BOOLEAN MODE in the WHERE clause when no results are returned in the initial search. This is still a poor solution as the results can be quite "noisy" and the relevance only semi-accurate for some search terms.

Any better ideas?
  Posted by Jaimie Sirovich on December 20, 2009
@John,

You may be able to use FT boolean for inclusion/selection and then FT natural for ORDER. This has the effect that noise words will not affect rank, but otherwise the ranking is natural. This is mostly correct and desirable.

Regards,
Jaimie.
Sign Up Login You must be logged in to post a comment.