2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 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) @@ ytext @@ 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_configpostgresql.conf 中的值適用於整個叢集使用相同的文字搜尋配置。要使叢集內使用不同的配置,但每個資料庫內使用相同的配置,請使用 ALTER DATABASE ... SET。否則,您可以在每個會話中設定 default_text_search_config

每個依賴於配置的文字搜尋函式都有一個可選的 regconfig 引數,因此可以顯式指定要使用的配置。default_text_search_config 僅在省略此引數時使用。

為了更容易地構建自定義文字搜尋配置,配置是基於更簡單的資料庫物件構建的。 PostgreSQL 的文字搜尋功能提供了四種類型的與配置相關的資料庫物件:

  • 文字搜尋解析器 將文件分解成詞元,並對每個詞元進行分類(例如,按單詞或數字)。

  • 文字搜尋詞典 將詞元轉換為規範形式並拒絕停用詞。

  • 文字搜尋模板 提供詞典的底層函式。(詞典只需指定一個模板和該模板的一組引數。)

  • 文字搜尋配置 選擇一個解析器和一組詞典,用於規範化解析器產生的詞元。

文字搜尋解析器和模板是基於低階 C 函式構建的;因此,開發新的解析器和模板需要 C 程式設計能力,並需要超級使用者許可權才能將其安裝到資料庫中。(在 PostgreSQL 發行版的 contrib/ 區域中有附加解析器和模板的示例。)由於詞典和配置只是對底層解析器和模板進行引數化和連線,因此建立新的詞典或配置不需要特殊許可權。本章後面將給出建立自定義詞典和配置的示例。

提交更正

如果您在文件中發現任何不正確的內容,與您在使用特定功能時的體驗不符,或者需要進一步澄清,請使用 此表單 報告文件問題。