支援的版本:目前 (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. 簡介 #

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

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

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

  • 它們不提供搜尋結果的排序(排名),這使得它們在找到數千個匹配文件時無效。

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

全文索引允許對文件進行預處理,並保存索引以供以後快速搜尋。預處理包括

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

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

  • 儲存針對搜尋最佳化的預處理文件。例如,每個文件都可以表示為正規化詞位的排序陣列。除了詞位之外,通常還希望儲存位置資訊以用於鄰近度排名,以便包含更密集查詢詞彙區域的文件的排名高於具有分散查詢詞彙的文件。

字典允許對符記的正規化方式進行細粒度控制。透過適當的字典,您可以

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

  • 使用 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 運算符的嵌套。 如果沒有括號,則 | 的綁定最弱,然後是 &,然後是 <->,而 ! 的綁定最強。

值得注意的是,當 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 的文字搜尋功能提供了四種類型的配置相關資料庫物件

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

  • 文字搜尋字典將語彙單元轉換為正規化的形式,並拒絕停用詞。

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

  • 文字搜尋配置選擇一個剖析器和一組字典,以用於正規化剖析器產生的語彙單元。

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

提交更正

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