支援版本:目前版本 (17) / 16 / 15 / 14 / 13
開發版本:devel
不再支援版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3

12.1. 簡介 #

全文檢索(或簡稱為文字搜尋)提供識別符合查詢的自然語言文件的能力,並且可以選擇依據與查詢的關聯性來排序它們。最常見的搜尋類型是找到所有包含給定的查詢詞彙的文件,並依據它們與查詢的相似度排序後回傳。 querysimilarity 的概念非常靈活,並且取決於具體的應用。最簡單的搜尋將 query 視為一組字詞,並將 similarity 視為查詢字詞在文件中出現的頻率。

文字搜尋運算子已經存在於資料庫多年。PostgreSQL 具有用於文字資料類型的 ~~*LIKEILIKE 運算子,但它們缺乏現代資訊系統所需的許多基本屬性

  • 即使對於英語,也沒有語言支援。 正規表示式是不夠的,因為它們無法輕易處理衍生詞,例如 satisfiessatisfy。 您可能會錯過包含 satisfies 的文件,儘管您可能希望在搜尋 satisfy 時找到它們。 可以使用 OR 搜尋多個衍生形式,但這很繁瑣且容易出錯(某些單字可能有數千個衍生詞)。

  • 它們不提供搜尋結果的排序(排名),這使得在找到數千個相符文件時它們的效率低下。

  • 它們往往很慢,因為沒有索引支援,因此它們必須為每次搜尋處理所有文件。

全文索引允許預先處理文件並儲存索引,以便稍後快速搜尋。 預先處理包括

  • 將文件解析為 tokens(詞彙單元)。 識別各種 tokens 類別(例如數字、單字、複合詞、電子郵件地址)非常有用,以便可以不同方式處理它們。 原則上,tokens 類別取決於具體的應用,但對於大多數目的而言,使用預定義的類別集就足夠了。PostgreSQL 使用parser(剖析器)執行此步驟。 提供了一個標準的剖析器,並且可以為特定需求建立自訂剖析器。

  • 將 tokens 轉換為 lexemes(詞素)。 詞素是一個字串,就像 token 一樣,但它已經過正規化,因此相同單字的不同形式會變得相似。 例如,正規化幾乎總是包括將大寫字母摺疊為小寫字母,並且通常涉及移除後綴(例如英語中的 ses)。 這允許搜尋找到相同單字的變體形式,而無需繁瑣地輸入所有可能的變體。 此外,此步驟通常會消除stop words(停用詞),這些詞非常常見,以至於它們對於搜尋沒有用處。 (簡而言之,tokens 是文件文字的原始片段,而詞素是被認為可用於索引和搜尋的字詞。)PostgreSQL 使用dictionaries(字典)執行此步驟。 提供了各種標準字典,並且可以為特定需求建立自訂字典。

  • 儲存針對搜尋優化的預先處理文件。 例如,每個文件可以表示為正規化詞素的排序陣列。 除了詞素之外,通常還需要儲存位置資訊,以用於proximity ranking(鄰近度排名),以便將包含更密集查詢字詞區域的文件分配到比具有分散查詢字詞的文件更高的排名。

字典允許對如何正規化 tokens 進行細粒度控制。 使用適當的字典,您可以

  • 定義不應編入索引的停用詞。

  • 使用 Ispell 將同義詞對應到單一單字。

  • 使用詞庫將短語對應到單一單字。

  • 使用 Ispell 字典將單字的不同變體對應到規範形式。

  • 使用 Snowball 詞幹還原規則將單字的不同變體對應到規範形式。

提供了一種資料類型 tsvector,用於儲存預先處理的文件,以及一種類型 tsquery,用於表示已處理的查詢(第 8.11 節)。 有許多函數和運算子可用於這些資料類型(第 9.13 節),其中最重要的是比對運算子 @@,我們將在 第 12.1.2 節中介紹它。 可以使用索引加速全文檢索(第 12.9 節)。

12.1.1. 什麼是文件? #

在全文檢索系統中,文件是搜尋的單位;例如,雜誌文章或電子郵件訊息。文本搜尋引擎必須能夠解析文件,並將詞位(關鍵字)與其父文件建立關聯。之後,這些關聯會被用來搜尋包含查詢詞的文件。

對於 PostgreSQL 內的搜尋,文件通常是資料庫表格中一列的文字欄位,或者可能是這些欄位的組合(串聯),可能儲存在多個表格中或動態取得。換句話說,可以從不同的部分建構文件以進行索引,並且可能不會作為整體儲存在任何地方。例如:

SELECT title || ' ' ||  author || ' ' ||  abstract || ' ' || body AS document
FROM messages
WHERE mid = 12;

SELECT m.title || ' ' || m.author || ' ' || m.abstract || ' ' || d.body AS document
FROM messages m, docs d
WHERE m.mid = d.did AND m.mid = 12;

注意

實際上,在這些範例查詢中,應該使用 coalesce 來防止單個 NULL 屬性導致整個文件的 NULL 結果。

另一種可能性是將文件儲存為檔案系統中的簡單文字檔案。在這種情況下,資料庫可用於儲存全文索引並執行搜尋,並且可以使用唯一的識別符號從檔案系統中檢索文件。然而,從資料庫外部檢索檔案需要超級使用者權限或特殊功能支援,因此這通常不如將所有資料保留在 PostgreSQL 中方便。此外,將所有內容保留在資料庫中可以輕鬆存取文件元資料,以協助索引和顯示。

為了文本搜尋的目的,每個文件都必須簡化為經過預處理的 tsvector 格式。搜尋和排名完全在文件的 tsvector 表示上執行 — 只有在選擇文件向使用者顯示時才需要檢索原始文本。因此,我們經常將 tsvector 視為文件,但它當然只是完整文件的緊湊表示。

12.1.2. 基本文本匹配 #

PostgreSQL 中的全文搜尋基於匹配運算子 @@,如果 tsvector(文件)與 tsquery(查詢)匹配,則返回 true。先寫入哪種資料類型無關緊要:

SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector @@ 'cat & rat'::tsquery;
 ?column?
----------
 t

SELECT 'fat & cow'::tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector;
 ?column?
----------
 f

正如上面的例子所暗示的那樣,tsquery 不僅僅是原始文本,就像 tsvector 一樣。 tsquery 包含搜尋詞,它們必須是已經標準化的詞位,並且可以使用 AND、OR、NOT 和 FOLLOWED BY 運算子組合多個詞。(有關語法詳細信息,請參閱 第 8.11.2 節。)函數 to_tsqueryplainto_tsqueryphraseto_tsquery 有助於將使用者編寫的文本轉換為適當的 tsquery,主要是通過標準化文本中出現的單詞。同樣,to_tsvector 用於解析和標準化文件字串。因此,在實踐中,文本搜尋匹配看起來更像這樣:

SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat');
 ?column?
----------
 t

請注意,如果寫成以下形式,此匹配將不會成功:

SELECT 'fat cats ate fat rats'::tsvector @@ to_tsquery('fat & rat');
 ?column?
----------
 f

因為這裡不會對單詞 rats 進行標準化。tsvector 的元素是詞位,它們被認為已經標準化,因此 rats 不匹配 rat

@@ 運算子也支援 text 輸入,允許在簡單情況下跳過將文本字串顯式轉換為 tsvectortsquery。可用的變體是:

tsvector @@ tsquery
tsquery  @@ tsvector
text @@ tsquery
text @@ text

我們已經看到了前兩個。形式 text @@ tsquery 等效於 to_tsvector(x) @@ y。形式 text @@ text 等效於 to_tsvector(x) @@ plainto_tsquery(y)

tsquery 中,&(AND)運算子指定其兩個參數都必須出現在文件中才能匹配。類似地,|(OR)運算子指定其參數中至少有一個必須出現,而 !(NOT)運算子指定其參數必須出現才能匹配。例如,查詢 fat & ! rat 匹配包含 fat 但不包含 rat 的文件。

可以使用 <-> (FOLLOWED BY) tsquery 運算子搜尋短語,該運算子僅在其參數具有相鄰且以給定順序的匹配項時才匹配。例如:

SELECT to_tsvector('fatal error') @@ to_tsquery('fatal <-> error');
 ?column?
----------
 t

SELECT to_tsvector('error is not fatal') @@ to_tsquery('fatal <-> error');
 ?column?
----------
 f

FOLLOWED BY 運算子有一個更通用的版本,其形式為 <N>,其中 N 是一個整數,代表匹配詞位的位置之間的差異。 <1><-> 相同,而 <2> 允許在匹配項之間恰好出現一個其他詞位,依此類推。 phraseto_tsquery 函數使用此運算子建構 tsquery,該 tsquery 可以在某些單詞是停用詞時匹配多詞短語。例如:

SELECT phraseto_tsquery('cats ate rats');
       phraseto_tsquery
-------------------------------
 'cat' <-> 'ate' <-> 'rat'

SELECT phraseto_tsquery('the cats ate the rats');
       phraseto_tsquery
-------------------------------
 'cat' <-> 'ate' <2> 'rat'

有時有用的一種特殊情況是,可以使用 <0> 來要求兩個模式匹配同一個單詞。

可以使用括號來控制 tsquery 運算子的巢狀結構。如果沒有括號,| 的綁定性最弱,然後是 &,然後是 <->! 的綁定性最強。

值得注意的是,在 FOLLOWED BY 運算子的參數中,AND/OR/NOT 運算子的意義與不在 FOLLOWED BY 運算子中時略有不同,因為在 FOLLOWED BY 中,比對的確切位置非常重要。例如,通常 !x 只比對不包含任何 x 的文件。但 !x <-> y 會比對 y,如果它不是緊接在 x 之後;文件中其他地方出現 x 並不會阻止比對。另一個例子是,x & y 通常只需要 xy 都出現在文件中某個地方,但 (x & y) <-> z 需要 xy 在相同位置比對,且緊接在 z 之前。因此,此查詢的行為與 x <-> z & y <-> z 不同,後者會比對包含兩個獨立序列 x zy z 的文件。(這個特定的查詢寫法是無用的,因為 xy 無法在相同位置比對;但在更複雜的情況下,例如前綴比對模式,這種形式的查詢可能會很有用。)

12.1.3. 設定 #

以上都是簡單的文字搜尋範例。如前所述,全文搜尋功能包括執行更多事情的能力:跳過索引某些詞語(停用詞)、處理同義詞,以及使用複雜的解析,例如,根據不只是空白的內容進行解析。此功能由文字搜尋設定控制。PostgreSQL 隨附了許多語言的預定義設定,您可以輕鬆建立自己的設定。(psql\dF 命令會顯示所有可用的設定。)

在安裝過程中,會選取適當的設定,並在 postgresql.conf 中相應地設定 default_text_search_config。如果您對整個叢集使用相同的文字搜尋設定,則可以使用 postgresql.conf 中的值。若要在整個叢集中使用不同的設定,但在任何一個資料庫中使用相同的設定,請使用 ALTER DATABASE ... SET。否則,您可以在每個連線階段中設定 default_text_search_config

每個依賴設定的文字搜尋函式都有一個可選的 regconfig 引數,以便可以明確指定要使用的設定。僅當省略此引數時,才會使用 default_text_search_config

為了更容易建立自訂文字搜尋設定,設定是從更簡單的資料庫物件建立的。PostgreSQL 的文字搜尋工具提供了四種與設定相關的資料庫物件類型

  • 文字搜尋剖析器將文件分解為 token 並對每個 token 進行分類(例如,作為單字或數字)。

  • 文字搜尋字典將 token 轉換為標準化形式並拒絕停用詞。

  • 文字搜尋範本提供字典的底層函式。(字典只是指定範本和範本的一組參數。)

  • 文字搜尋設定選取剖析器和一組字典,用於標準化剖析器產生的 token。

文字搜尋剖析器和範本是從低階 C 函式建立的;因此,開發新的剖析器和範本需要 C 程式設計能力,並且需要超級使用者權限才能將其安裝到資料庫中。(在 PostgreSQL 發行版本的 contrib/ 區域中有附加剖析器和範本的範例。)由於字典和設定只是將一些底層剖析器和範本參數化並連接在一起,因此建立新的字典或設定不需要特殊權限。建立自訂字典和設定的範例將在本章稍後出現。

提交更正

如果您在文件中發現任何不正確、與您特定功能的使用經驗不符或需要進一步澄清的地方,請使用此表格來回報文件問題。