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.4. 附加功能 #

本節介紹與文字搜尋相關的其他有用函式和運算子。

12.4.1. 操作文件 #

第 12.3.1 節介紹瞭如何將原始文字文件轉換為 tsvector 值。PostgreSQL 還提供了函式和運算子,可用於操作已為 tsvector 形式的文件。

tsvector || tsvector

tsvector 連線運算子返回一個向量,該向量結合了作為引數提供的兩個向量的詞元和位置資訊。連線過程中保留了位置和權重標籤。右側向量中的位置會根據左側向量中提到的最大位置進行偏移,因此結果在很大程度上等同於對兩個原始文件字串的連線執行 to_tsvector 的結果。(等價並非精確,因為從左側引數末尾刪除的任何停用詞都不會影響結果,而如果在文字連線中使用,它們會影響右側引數中詞元的位置。)

使用向量形式進行連線而不是在應用 to_tsvector 之前連線文字的一個優點是,您可以使用不同的配置來解析文件的不同部分。此外,因為 setweight 函式以相同的方式標記給定向量的所有詞元,所以如果要使用不同的權重標記文件的不同部分,則必須在連線之前解析文字並執行 setweight

setweight(vector tsvector, weight "char") returns tsvector

setweight 返回輸入向量的副本,其中每個位置都用指定的 weight 標記,可以是 ABCD。(D 是新向量的預設值,因此在輸出中不顯示。)連線向量時會保留這些標籤,從而允許透過排名函式為文件的不同部分分配不同的權重。

請注意,權重標籤適用於位置,而不是詞元。如果輸入向量已剝離位置資訊,則 setweight 不執行任何操作。

length(vector tsvector) returns integer

返回向量中儲存的詞元數。

strip(vector tsvector) returns tsvector

返回一個列出與給定向量相同詞元的向量,但不包含任何位置或權重資訊。結果通常比未剝離的向量小得多,但用處也較少。相關性排名在剝離的向量上不如在未剝離的向量上效果好。此外,<->(後跟)tsquery 運算子永遠不會匹配剝離的輸入,因為它無法確定詞元出現之間的距離。

文字搜尋函式列表可在表 9.43 中找到。

12.4.2. 操作查詢 #

第 12.3.2 節介紹瞭如何將原始文字查詢轉換為 tsquery 值。PostgreSQL 還提供了函式和運算子,可用於操作已為 tsquery 形式的查詢。

tsquery && tsquery

返回兩個給定查詢的 AND 組合。

tsquery || tsquery

返回兩個給定查詢的 OR 組合。

!! tsquery

返回給定查詢的否定 (NOT)。

tsquery <-> tsquery

返回一個查詢,該查詢搜尋第一個給定查詢緊接著第二個給定查詢的匹配,使用 <->(後跟)tsquery 運算子。例如

SELECT to_tsquery('fat') <-> to_tsquery('cat | rat');
          ?column?
----------------------------
 'fat' <-> ( 'cat' | 'rat' )
tsquery_phrase(query1 tsquery, query2 tsquery [, distance integer ]) returns tsquery

返回一個查詢,該查詢搜尋第一個給定查詢的匹配,然後在 distance 個詞元的距離處搜尋第二個給定查詢的匹配,使用 <N> tsquery 運算子。例如

SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10);
  tsquery_phrase
------------------
 'fat' <10> 'cat'
numnode(query tsquery) returns integer

返回 tsquery 中的節點數(詞元加上運算子)。此函式有助於確定 query 是否有意義(返回 > 0),或者是否僅包含停用詞(返回 0)。示例

SELECT numnode(plainto_tsquery('the any'));
NOTICE:  query contains only stopword(s) or doesn't contain lexeme(s), ignored
 numnode
---------
       0

SELECT numnode('foo & bar'::tsquery);
 numnode
---------
       3
querytree(query tsquery) returns text

返回 tsquery 中可用於搜尋索引的部分。此函式有助於檢測不可索引的查詢,例如僅包含停用詞或僅否定項的查詢。例如

SELECT querytree(to_tsquery('defined'));
 querytree
-----------
 'defin'

SELECT querytree(to_tsquery('!defined'));
 querytree
-----------
 T

12.4.2.1. 查詢重寫 #

ts_rewrite 系列函式搜尋給定 tsquery 中目標子查詢的出現,並將每個出現替換為替代子查詢。本質上,此操作是 tsquery 特定的子字串替換版本。目標和替代組合可以看作是查詢重寫規則。此類重寫規則的集合可以是一個強大的搜尋輔助工具。例如,您可以使用同義詞來擴充套件搜尋(例如,new yorkbig applenycgotham),或者縮小搜尋範圍以將使用者引導至某個熱門話題。此功能與同義詞詞典(第 12.6.4 節)在功能上存在一些重疊。但是,您可以即時修改一組重寫規則,而無需重新索引,而更新同義詞需要重新索引才能生效。

ts_rewrite (query tsquery, target tsquery, substitute tsquery) returns tsquery

此形式的 ts_rewrite 僅應用單個重寫規則: wherever target appears in query, it is replaced by substitute。例如

SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery);
 ts_rewrite
------------
 'b' & 'c'
ts_rewrite (query tsquery, select text) returns tsquery

此形式的 ts_rewrite 接受一個初始 query 和一個 SQL select 命令,該命令以文字字串形式給出。select 必須返回兩列 tsquery 型別。對於 select 結果的每一行,query 中的第一列值(目標)的出現將被第二列值(替代)替換。例如

CREATE TABLE aliases (t tsquery PRIMARY KEY, s tsquery);
INSERT INTO aliases VALUES('a', 'c');

SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases');
 ts_rewrite
------------
 'b' & 'c'

請注意,當以這種方式應用多個重寫規則時,應用的順序可能很重要;因此,在實踐中,您將希望源查詢 ORDER BY 某個排序鍵。

讓我們考慮一個現實的天文學示例。我們將使用表驅動的重寫規則來擴充套件查詢 supernovae

CREATE TABLE aliases (t tsquery primary key, s tsquery);
INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn'));

SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases');
           ts_rewrite
---------------------------------
 'crab' & ( 'supernova' | 'sn' )

我們只需透過更新表即可更改重寫規則。

UPDATE aliases
SET s = to_tsquery('supernovae|sn & !nebulae')
WHERE t = to_tsquery('supernovae');

SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases');
                 ts_rewrite
---------------------------------------------
 'crab' & ( 'supernova' | 'sn' & !'nebula' )

當存在許多重寫規則時,重寫可能會很慢,因為它會為每個規則檢查可能的匹配。為了過濾掉明顯的非候選規則,我們可以使用 tsquery 型別的包含運算子。在下面的示例中,我們僅選擇可能匹配原始查詢的規則。

SELECT ts_rewrite('a & b'::tsquery,
                  'SELECT t,s FROM aliases WHERE ''a & b''::tsquery @> t');
 ts_rewrite
------------
 'b' & 'c'

12.4.3. 自動更新的觸發器 #

注意

本節描述的方法已被儲存的生成列取代,如第 12.2.2 節中所述。

當使用單獨的列儲存文件的 tsvector 表示時,有必要建立一個觸發器來在文件內容列更改時更新 tsvector 列。為此提供了兩個內建觸發器函式,或者您可以編寫自己的觸發器。

tsvector_update_trigger(tsvector_column_name,​ config_name, text_column_name [, ... ])
tsvector_update_trigger_column(tsvector_column_name,​ config_column_name, text_column_name [, ... ])

這些觸發器函式在 CREATE TRIGGER 命令中指定的引數控制下,自動從一個或多個文字列計算 tsvector 列。其用法示例為

CREATE TABLE messages (
    title       text,
    body        text,
    tsv         tsvector
);

CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON messages FOR EACH ROW EXECUTE FUNCTION
tsvector_update_trigger(tsv, 'pg_catalog.english', title, body);

INSERT INTO messages VALUES('title here', 'the body text is here');

SELECT * FROM messages;
   title    |         body          |            tsv
------------+-----------------------+----------------------------
 title here | the body text is here | 'bodi':4 'text':5 'titl':1

SELECT title, body FROM messages WHERE tsv @@ to_tsquery('title & body');
   title    |         body
------------+-----------------------
 title here | the body text is here

建立此觸發器後,對 titlebody 的任何更改都將自動反映到 tsv 中,而無需應用程式擔心。

第一個觸發器引數必須是將被更新的 tsvector 列的名稱。第二個引數指定用於執行轉換的文字搜尋配置。對於 tsvector_update_trigger,配置名稱僅作為第二個觸發器引數給出。它必須像上面那樣使用模式限定,以便觸發器行為不會因 search_path 的更改而改變。對於 tsvector_update_trigger_column,第二個觸發器引數是另一個表列的名稱,該列必須是 regconfig 型別。這允許按行選擇配置。其餘引數是文字列(textvarcharchar 型別)的名稱。這些將按照給定的順序包含在文件中。NULL 值將被跳過(但其他列仍會被索引)。

這些內建觸發器的侷限性在於它們以相同的方式處理所有輸入列。要以不同方式處理列(例如,為標題賦予與正文不同的權重),則需要編寫自定義觸發器。以下是使用 PL/pgSQL 作為觸發器語言的示例

CREATE FUNCTION messages_trigger() RETURNS trigger AS $$
begin
  new.tsv :=
     setweight(to_tsvector('pg_catalog.english', coalesce(new.title,'')), 'A') ||
     setweight(to_tsvector('pg_catalog.english', coalesce(new.body,'')), 'D');
  return new;
end
$$ LANGUAGE plpgsql;

CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
    ON messages FOR EACH ROW EXECUTE FUNCTION messages_trigger();

請記住,在觸發器中建立 tsvector 值時顯式指定配置名稱非常重要,這樣列的內容就不會受到 default_text_search_config 更改的影響。未能這樣做很可能會導致問題,例如轉儲和恢復後搜尋結果發生變化。

12.4.4. 收集文件統計資訊 #

函式 ts_stat 有助於檢查您的配置和查詢停用詞候選。

ts_stat(sqlquery text, [ weights text, ]
        OUT word text, OUT ndoc integer,
        OUT nentry integer) returns setof record

sqlquery 是一個文字值,其中包含一個 SQL 查詢,該查詢必須返回單個 tsvector 列。ts_stat 執行查詢並返回包含在 tsvector 資料中的每個不同詞元(單詞)的統計資訊。返回的列是

  • word text — 詞元的值

  • ndoc integer — 單詞出現的文件(tsvector)數

  • nentry integer — 單詞出現次數的總數

如果提供了 weights,則僅計算具有這些權重之一的出現次數。

例如,查詢文件集合中最頻繁的十個單詞。

SELECT * FROM ts_stat('SELECT vector FROM apod')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;

相同,但僅計算權重為 AB 的單詞出現次數。

SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;

提交更正

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