預設情況下或使用 IN NATURAL LANGUAGE MODE
修飾詞,MATCH()
函數會針對文字集合執行字串的自然語言搜尋。集合是包含在 FULLTEXT
索引中的一或多個欄位的集合。搜尋字串會作為 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
子句中時(如先前範例所示),只要符合以下條件,傳回的資料列就會自動先以最高相關性排序
不得有明確的
ORDER BY
子句。搜尋必須使用全文索引掃描而不是表格掃描來執行。
如果查詢聯結表格,則全文索引掃描必須是聯結中最左側的非常數表格。
考量到剛才列出的條件,當需要或想要時,指定使用 ORDER BY
明確排序順序通常會更省力。
相關性值是非負數的浮點數。零相關性表示沒有相似度。相關性的計算基礎為資料列 (文件) 中的單字數、資料列中的唯一單字數、集合中的單字總數以及包含特定單字的資料列數。
術語「文件」可以與術語「資料列」互換使用,這兩個術語都指的是資料列的索引部分。術語「集合」指的是索引欄位,並涵蓋所有資料列。
若要單純計算相符項目,可以使用如下查詢
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)
第一個查詢會進行一些額外的工作(按相關性排序結果),但也可能會使用以 WHERE
子句為基礎的索引查詢。如果搜尋比對的資料列很少,索引查詢可能會使第一個查詢更快。如果搜尋詞彙存在於大多數資料列中,第二個查詢會執行完整的表格掃描,這可能比索引查詢更快。
對於自然語言全文檢索,MATCH()
函數中命名的欄位必須與表格中某些 FULLTEXT
索引中包含的欄位相同。對於先前的查詢,請注意,MATCH()
函數中命名的欄位 (title
和 body
) 與 article
表格的 FULLTEXT
索引定義中命名的欄位相同。若要分別搜尋 title
或 body
,您需要為每個欄位建立個別的 FULLTEXT
索引。
您也可以執行布林搜尋或使用查詢擴展進行搜尋。這些搜尋類型在第 14.9.2 節「布林全文搜尋」和第 14.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()
:一次在 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)
以雙引號 ("
) 字元括住的片語只會比對到包含該片語完全依照輸入內容的列。全文引擎會將片語分割成單字,並在 FULLTEXT
索引中搜尋這些單字。非單字字元不需要完全比對:片語搜尋只需要比對包含與片語完全相同的單字,並且順序相同。例如,"test phrase"
會比對到 "test, phrase"
。如果片語中沒有任何單字在索引中,結果會是空的。例如,如果所有單字都是停用詞,或短於索引單字的最小長度,結果會是空的。
MySQL 的 FULLTEXT
實作會將任何真實單字字元(字母、數字和底線)序列視為單字。該序列也可能包含單引號 ('
),但不能連續超過一個。這表示 aaa'bbb
會被視為一個單字,但 aaa''bbb
會被視為兩個單字。FULLTEXT
解析器會移除單字開頭或結尾的單引號;'aaa'bbb'
會被解析為 aaa'bbb
。
內建的 FULLTEXT
解析器會透過尋找特定的分隔字元來判斷單字的開始和結束位置;例如,
(空格)、,
(逗號) 和 .
(句點)。如果單字沒有以分隔符號分隔(例如在中文中),則內建的 FULLTEXT
解析器無法判斷單字的開始或結束位置。為了能夠將這類語言的單字或其他索引詞加入到使用內建 FULLTEXT
解析器的 FULLTEXT
索引中,您必須預先處理它們,使其以某些任意分隔符號分隔。或者,您可以使用 ngram 解析器外掛程式(用於中文、日文或韓文)或 MeCab 解析器外掛程式(用於日文)建立 FULLTEXT
索引。
您可以編寫一個外掛程式來取代內建的全文解析器。如需詳細資訊,請參閱MySQL 外掛程式 API。如需範例解析器外掛程式原始程式碼,請參閱 MySQL 原始程式碼散佈的 plugin/fulltext
目錄。
在全文搜尋中,某些單字會被忽略
任何過短的單字都會被忽略。全文搜尋找到的單字預設最小長度,對於
InnoDB
搜尋索引為三個字元,對於MyISAM
則為四個字元。您可以在建立索引之前設定組態選項來控制截止點:InnoDB
搜尋索引的innodb_ft_min_token_size
組態選項,或是MyISAM
的ft_min_word_len
。注意此行為不適用於使用 ngram 解析器的
FULLTEXT
索引。對於 ngram 解析器,token 長度是由ngram_token_size
選項定義。停用詞清單中的單字會被忽略。停用詞是指一些非常常見而被視為沒有語義價值的單字,例如「“the”」或「“some”」。有一個內建的停用詞清單,但可以由使用者定義的清單覆寫。停用詞清單和相關的組態選項對於
InnoDB
搜尋索引和MyISAM
搜尋索引是不同的。停用詞處理由組態選項innodb_ft_enable_stopword
、innodb_ft_server_stopword_table
和innodb_ft_user_stopword_table
(用於InnoDB
搜尋索引),以及ft_stopword_file
(用於MyISAM
搜尋索引)控制。
請參閱第 14.9.4 節「全文停用詞」以檢視預設停用詞清單,以及如何變更它們。預設的最小單字長度可以在第 14.9.6 節「微調 MySQL 全文搜尋」中所述的方式進行變更。
集合和查詢中的每個正確單字都會根據其在集合或查詢中的重要性進行加權。因此,出現在許多文件中的單字權重會較低,因為它在這個特定集合中的語義價值較低。反之,如果單字很少見,它會收到較高的權重。單字的權重會結合在一起,以計算列的關聯性。這種技術在大型集合中最有效。
對於非常小的資料表,單字分佈無法充分反映其語義價值,而且這種模型有時可能會在 MyISAM
資料表的搜尋索引中產生奇怪的結果。例如,雖然單字「“MySQL”」出現在先前顯示的 articles
資料表的每一列中,但在 MyISAM
搜尋索引中搜尋該單字不會產生任何結果。
mysql> SELECT * FROM articles
-> WHERE MATCH (title,body)
-> AGAINST ('MySQL' IN NATURAL LANGUAGE MODE);
Empty set (0.00 sec)
搜尋結果是空的,因為單字「“MySQL”」至少出現在 50% 的列中,因此實際上被視為停用詞。此篩選技術更適合大型資料集,因為您可能不希望結果集從 1GB 的資料表中傳回每隔一列,而不是對於小型資料集,因為它可能會導致常用詞的結果不佳。
當您第一次嘗試全文搜尋以了解其運作方式時,50% 的門檻可能會讓您感到驚訝,並使 InnoDB
資料表更適合用來實驗全文搜尋。如果您建立一個 MyISAM
資料表,並在其中插入一到兩列文字,則文字中的每個單字都會至少出現在 50% 的列中。因此,在資料表包含更多列之前,任何搜尋都不會傳回任何結果。需要繞過 50% 限制的使用者可以在 InnoDB
資料表上建立搜尋索引,或使用第 14.9.2 節「布林全文搜尋」中說明的布林搜尋模式。