全文檢索(或簡稱為文字搜尋)提供識別符合查詢的自然語言文件的能力,並且可以選擇依據與查詢的關聯性來排序它們。最常見的搜尋類型是找到所有包含給定的查詢詞彙的文件,並依據它們與查詢的相似度排序後回傳。 query
和 similarity
的概念非常靈活,並且取決於具體的應用。最簡單的搜尋將 query
視為一組字詞,並將 similarity
視為查詢字詞在文件中出現的頻率。
文字搜尋運算子已經存在於資料庫多年。PostgreSQL 具有用於文字資料類型的 ~
、~*
、LIKE
和 ILIKE
運算子,但它們缺乏現代資訊系統所需的許多基本屬性
即使對於英語,也沒有語言支援。 正規表示式是不夠的,因為它們無法輕易處理衍生詞,例如 satisfies
和 satisfy
。 您可能會錯過包含 satisfies
的文件,儘管您可能希望在搜尋 satisfy
時找到它們。 可以使用 OR
搜尋多個衍生形式,但這很繁瑣且容易出錯(某些單字可能有數千個衍生詞)。
它們不提供搜尋結果的排序(排名),這使得在找到數千個相符文件時它們的效率低下。
它們往往很慢,因為沒有索引支援,因此它們必須為每次搜尋處理所有文件。
全文索引允許預先處理文件並儲存索引,以便稍後快速搜尋。 預先處理包括
將文件解析為 tokens(詞彙單元)。 識別各種 tokens 類別(例如數字、單字、複合詞、電子郵件地址)非常有用,以便可以不同方式處理它們。 原則上,tokens 類別取決於具體的應用,但對於大多數目的而言,使用預定義的類別集就足夠了。PostgreSQL 使用parser(剖析器)執行此步驟。 提供了一個標準的剖析器,並且可以為特定需求建立自訂剖析器。
將 tokens 轉換為 lexemes(詞素)。 詞素是一個字串,就像 token 一樣,但它已經過正規化,因此相同單字的不同形式會變得相似。 例如,正規化幾乎總是包括將大寫字母摺疊為小寫字母,並且通常涉及移除後綴(例如英語中的 s
或 es
)。 這允許搜尋找到相同單字的變體形式,而無需繁瑣地輸入所有可能的變體。 此外,此步驟通常會消除stop words(停用詞),這些詞非常常見,以至於它們對於搜尋沒有用處。 (簡而言之,tokens 是文件文字的原始片段,而詞素是被認為可用於索引和搜尋的字詞。)PostgreSQL 使用dictionaries(字典)執行此步驟。 提供了各種標準字典,並且可以為特定需求建立自訂字典。
儲存針對搜尋優化的預先處理文件。 例如,每個文件可以表示為正規化詞素的排序陣列。 除了詞素之外,通常還需要儲存位置資訊,以用於proximity ranking(鄰近度排名),以便將包含更“密集”查詢字詞區域的文件分配到比具有分散查詢字詞的文件更高的排名。
字典允許對如何正規化 tokens 進行細粒度控制。 使用適當的字典,您可以
定義不應編入索引的停用詞。
使用 Ispell 將同義詞對應到單一單字。
使用詞庫將短語對應到單一單字。
使用 Ispell 字典將單字的不同變體對應到規範形式。
使用 Snowball 詞幹還原規則將單字的不同變體對應到規範形式。
提供了一種資料類型 tsvector
,用於儲存預先處理的文件,以及一種類型 tsquery
,用於表示已處理的查詢(第 8.11 節)。 有許多函數和運算子可用於這些資料類型(第 9.13 節),其中最重要的是比對運算子 @@
,我們將在 第 12.1.2 節中介紹它。 可以使用索引加速全文檢索(第 12.9 節)。
在全文檢索系統中,文件是搜尋的單位;例如,雜誌文章或電子郵件訊息。文本搜尋引擎必須能夠解析文件,並將詞位(關鍵字)與其父文件建立關聯。之後,這些關聯會被用來搜尋包含查詢詞的文件。
對於 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
視為文件,但它當然只是完整文件的緊湊表示。
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_tsquery
、plainto_tsquery
和 phraseto_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
輸入,允許在簡單情況下跳過將文本字串顯式轉換為 tsvector
或 tsquery
。可用的變體是:
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
通常只需要 x
和 y
都出現在文件中某個地方,但 (x & y) <-> z
需要 x
和 y
在相同位置比對,且緊接在 z
之前。因此,此查詢的行為與 x <-> z & y <-> z
不同,後者會比對包含兩個獨立序列 x z
和 y z
的文件。(這個特定的查詢寫法是無用的,因為 x
和 y
無法在相同位置比對;但在更複雜的情況下,例如前綴比對模式,這種形式的查詢可能會很有用。)
以上都是簡單的文字搜尋範例。如前所述,全文搜尋功能包括執行更多事情的能力:跳過索引某些詞語(停用詞)、處理同義詞,以及使用複雜的解析,例如,根據不只是空白的內容進行解析。此功能由文字搜尋設定控制。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/
區域中有附加剖析器和範本的範例。)由於字典和設定只是將一些底層剖析器和範本參數化並連接在一起,因此建立新的字典或設定不需要特殊權限。建立自訂字典和設定的範例將在本章稍後出現。
如果您在文件中發現任何不正確、與您特定功能的使用經驗不符或需要進一步澄清的地方,請使用此表格來回報文件問題。