Like Share Discussion Bookmark Smile

J.J. Huang   2020-07-10   MySQL   瀏覽次數:

MySQL - 第十九章 | 全文本搜索(下)

使用查詢擴展

查詢擴展用來設法放寬所返回的全文本搜索結果的範圍。考慮下面 的情況。你想找出所有提到anvils的注釋。只有一個注釋包含詞anvils,但你還想找出可能與你的搜索有關的所有其他行,即使它們不包含詞anvils。

這也是查詢擴展的一項任務。在使用查詢擴展時,MySQL對資料和索引進行兩遍掃描來完整搜索:

  • 首先,進行一個基本的全文本搜索,找出與搜索條件匹配的所有行;
  • 其次,MySQL檢查這些匹配行並選擇所有有用的詞(我們將會簡要地解釋MySQL如何斷定什麼有用,什麼無用)。
  • 再其次,MySQL再次進行全文本搜索,這次不僅使用原來的條件,而且還使用所有有用的詞。

利用查詢擴展,能找出可能相關的結果,即使它們並不精確包含所查找的詞。

首先進行一個簡單的全文本搜索,沒有查詢擴展:

1
2
3
4
5
6
7
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('anvils');
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| note_text |
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| Multiple customer returns, anvils failing to drop fast enough or falling backwards on purchaser. Recommend that customer considers using heavier anvils. |
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.06 sec)

分析:只有一行包含詞anvils,因此只返回一行。

下面是相同的搜索,這次使用查詢擴展:

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('anvils' WITH QUERY EXPANSION);
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| note_text |
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| Multiple customer returns, anvils failing to drop fast enough or falling backwards on purchaser. Recommend that customer considers using heavier anvils. |
| Customer complaint:Sticks not individually wrapped, too easy to mistakenly detonate all at once.Recommend individual wrapping. |
| Customer complaint:Not heavy enough to generate flying stars around head of victim. If being purchased for dropping, recommend ANV02 or ANV03 instead. |
| Please note that no returns will be accepted if safe opened using explosives. |
| Customer complaint: rabbit has been able to detect trap, food apparently less effective now. |
| Customer complaint:Circular hole in safe floor can apparently be easily cut with handsaw. |
| Matches not included, recommend purchase of matches or detonator (item DTNTR). |
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
7 rows in set (0.01 sec)

分析:這次返回了7行。第一行包含詞anvils,因此等級最高。第二行與anvils無關,但因為它包含第一行中的兩個詞(customer和recommend),所以也被檢索出來。第3行也包含這兩個相同的詞,但它們在文本中的位置更靠後且分開得更遠,因此也包含這一行,但等級為第三。第三行確實也沒有涉及anvils(按它們的產品名)。

正如所見,查詢擴展極大地增加了返回的行數,但這樣做也增加了你實際上並不想要的行的數目。

提示:行越多越好 表中的行越多(這些行中的文本就越多),使用查詢擴展返回的結果越好。

布林文本搜索

MySQL支持全文本搜索的另外一種形式,稱為布林方式(boolean mode)。以布林方式,可以提供關於如下內容的細節:

  • 要匹配的詞;
  • 要排斥的詞(如果某行包含這個詞,則不返回該行,即使它包含其他指定的詞也是如此);
  • 排列提示(指定某些詞比其他詞更重要,更重要的詞等級更高);
  • 表達式分組;
  • 另外一些內容。

提示:即使沒有FULLTEXT索引也可以使用 布林方式不同於迄今為止使用的全文本搜索語法的地方在於,即使沒有定義FULLTEXT索引,也可以使用它。但這是一種非常緩慢的操作(其性能將隨著資料量的增加而降低)。

1
2
3
4
5
6
7
8
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('heavy' IN BOOLEAN MODE);
+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| note_text |
+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| Item is extremely heavy. Designed for dropping, not recommended for use with slings, ropes, pulleys, or tightropes. |
| Customer complaint:Not heavy enough to generate flying stars around head of victim. If being purchased for dropping, recommend ANV02 or ANV03 instead. |
+---------------------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.01 sec)

分析:此全文本搜索檢索包含詞heavy的所有行(有兩行)。其中使用了關鍵字IN BOOLEAN MODE,但實際上沒有指定布林操作符,因此,其結果與沒有指定布林方式的結果相同。

說明:IN BOOLEAN MODE的行為差異 雖然這個例子的結果與沒有IN BOOLEAN MODE的相同,但其行為有一個重要的差別(即使在這個特殊的例子沒有表現出來)。

為了匹配包含heavy但不包含任意以rope開始的詞的行:

1
2
3
4
5
6
7
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('heavy -rope*' IN BOOLEAN MODE);
+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| note_text |
+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| Customer complaint:Not heavy enough to generate flying stars around head of victim. If being purchased for dropping, recommend ANV02 or ANV03 instead. |
+---------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

分析:這次只返回一行。這一次仍然匹配詞heavy,但-rope明確地指示MySQL排除包含rope(任何以rope開始的詞,包括ropes)的行,這就是為什麼上一個例子中的第一行被排除的原因。

目前介紹了兩個全文本搜索布林操作符-和,-排除一個詞,而是截斷操作符(可想象為用於詞尾的一個萬用字元)。

布林操作符 說明
+ 包含,詞必須存在
- 排除,詞必須不出現
> 包含,而且增加等級值
< 包含,且減少等級值
() 把詞組成子表達式(允許這些子表達式作為一個組被包含、排除、排列等)
~ 取消一個詞的排序值
* 詞尾的萬用字元
"" 定義一個短語(與單個詞的列表不一樣,它匹配整個短語以便包含或排除這個短語)

1
2
3
4
5
6
7
8
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('+rabbit +bait' IN BOOLEAN MODE);
+----------------------------------------------------------------------------------------------------------------------+
| note_text |
+----------------------------------------------------------------------------------------------------------------------+
| Quantity varies, sold by the sack load.
All guaranteed to be bright and orange, and suitable for use as rabbit bait. |
+----------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

分析:這個搜索匹配包含詞rabbit和bait的行。

1
2
3
4
5
6
7
8
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('rabbit bait' IN BOOLEAN MODE);
+----------------------------------------------------------------------------------------------------------------------+
| note_text |
+----------------------------------------------------------------------------------------------------------------------+
| Quantity varies, sold by the sack load.All guaranteed to be bright and orange, and suitable for use as rabbit bait. |
| Customer complaint: rabbit has been able to detect trap, food apparently less effective now. |
+----------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

分析:沒有指定操作符,這個搜索匹配包含rabbit和bait中的至少一個詞的行。

1
2
3
4
5
6
7
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('"rabbit bait"' IN BOOLEAN MODE);
+----------------------------------------------------------------------------------------------------------------------+
| note_text |
+----------------------------------------------------------------------------------------------------------------------+
| Quantity varies, sold by the sack load.All guaranteed to be bright and orange, and suitable for use as rabbit bait. |
+----------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

分析:這個搜索匹配短語rabbit bait而不是匹配兩個詞rabbit和bait。

1
2
3
4
5
6
7
8
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('>rabbit <carrot"' IN BOOLEAN MODE);
+----------------------------------------------------------------------------------------------------------------------+
| note_text |
+----------------------------------------------------------------------------------------------------------------------+
| Quantity varies, sold by the sack load.All guaranteed to be bright and orange, and suitable for use as rabbit bait. |
| Customer complaint: rabbit has been able to detect trap, food apparently less effective now. |
+----------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

分析:匹配rabbit和carrot,增加前者的等級,降低後者的等級。

1
2
3
4
5
6
7
mysql> SELECT note_text FROM productnotes WHERE Match(note_text) Against('+safe +(<combination)' IN BOOLEAN MODE);
+---------------------------------------------------------------------------------------------------------------------------------------------------+
| note_text |
+---------------------------------------------------------------------------------------------------------------------------------------------------+
| Safe is combination locked, combination not provided with safe.This is rarely a problem as safes are typically blown up or dropped by customers. |
+---------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

分析:這個搜索匹配詞safe和combination,降低後者的等級。

說明:排列而不排序 在布林方式中,不按等級值降序排序返回的行。

全文本搜索的使用說明

關於全文本搜索的某些重要的說明。

  • 在索引全文本資料時,短詞被忽略且從索引中排除。短詞定義為那些具有3個或3個以下字符的詞(如果需要,這個數目可以更改)。
  • MySQL帶有一個內建的非用詞(stopword)列表,這些詞在索引全文本資料時總是被忽略。
  • 許多詞出現的頻率很高,搜索它們沒有用處(返回太多的結果)。因此,MySQL規定了一條50%規則,如果一個詞出現在50%以上的行中,則將它作為一個非用詞忽略。50%規則不用於IN BOOLEAN MODE。
  • 如果表中的行數少於3行,則全文本搜索不返回結果(因為每個詞或者不出現,或者至少出現在50%的行中)。
  • 忽略詞中的單引號。例如,don’t索引為dont。
  • 不具有詞分隔符(包括日語和漢語)的語言不能恰當地返回全文本搜索結果。
  • 如前所述,僅在MyISAM資料庫引擎中支持全文本搜索。

說明:沒有鄰近操作符 鄰近搜索是許多全文本搜索支持的一個特性,它能搜索相鄰的詞(在相同的句子中、相同的段落中或者在特定數目的詞的部分中,等等)。MySQL全文本搜索現在還不支持鄰近操作符,不過未來的版本有支持這種操作符的計劃。

結語

這邊主要學到如何使用MySQL的 Match()和Against()函數進行全文本搜索,還有查詢擴展。我只能說這塊我可以說完全沒碰過。希望在將來有機會用到這樣的語法來應用在專案上面。

註:以上參考了
MySQL必知必会 MySQL Crash Course