文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 參考手冊  /  ...  /  自然語言全文搜尋

14.9.1 自然語言全文搜尋

預設情況下或使用 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_csutf8mb4_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() 函數中命名的資料行 (titlebody) 與 article 資料表 FULLTEXT 索引的定義中命名的資料行相同。若要單獨搜尋 titlebody,您會為每個資料行建立個別的 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 搜尋索引的停止詞處理由組態選項 innodb_ft_enable_stopwordinnodb_ft_server_stopword_tableinnodb_ft_user_stopword_table 控制,而 MyISAM 則由 ft_stopword_file 控制。

請參閱第 14.9.4 節,「全文停止詞」,以檢視預設的停止詞清單,以及如何變更它們。預設的最小單字長度可依照第 14.9.6 節,「微調 MySQL 全文搜尋」所述方式變更。

集合和查詢中的每個正確單字都會根據其在集合或查詢中的重要性進行加權。因此,出現在許多文件中的單字權重會較低,因為它在這個特定的集合中語義價值較低。反之,如果單字很少見,則會獲得較高的權重。單字的權重會組合起來計算資料列的關聯性。此技術適用於大型集合。

MyISAM 限制

對於非常小的資料表,單字分佈無法充分反映其語義價值,此模型有時可能會為 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 節,「布林全文搜尋」中說明的布林搜尋模式。