PostgreSQL pg_trgm

2021-09-16 17:07 更新
F.31.1. Trigram(或者 Trigraph)概念
F.31.2. 函數(shù)和操作符
F.31.3. GUC 參數(shù)
F.31.4. 索引支持
F.31.5. 文本搜索集成
F.31.6. 參考

pg_trgm模塊提供用于決定基于 trigram 匹配的字母數(shù)字文本相似度的函數(shù)和操作符,以及支持快速搜索相似字符串的索引操作符類。

該模塊被認(rèn)為是trusted,也就是說,它可以由對(duì)當(dāng)前數(shù)據(jù)庫(kù)具有CREATE權(quán)限的非超級(jí)用戶安裝。

F.31.1. Trigram(或者 Trigraph)概念

一個(gè) trigram 是從一個(gè)字符串中取出的由三個(gè)連續(xù)字符組成的組。我們可以通過對(duì)兩個(gè)字符串之間共享的 trigram 計(jì)數(shù)來度量它們的相似度。這種簡(jiǎn)單的思想已經(jīng)成為在很多自然語言中度量詞相似度的有效方法。

注意

在從一個(gè)字符串中提取 trigram 時(shí),pg_trgm會(huì)忽略非詞字符(非字母數(shù)字)。在決定字符串中所含的 trigram 集合時(shí),每一個(gè)詞被認(rèn)為具有兩個(gè)空格前綴和一個(gè)空格后綴。例如,字符串cat中的 trigram 集合是: c、 ca、 cat以及 at 。 字符串 foo|bar中的 trigram 集合是: f、 fofoo、 oo 、 b、 ba、 bar以及 ar 。

F.31.2. 函數(shù)和操作符

pg_trgm模塊所提供的函數(shù)如表 F.24中所示,操作符則顯示在表 F.25中。

表 F.24. pg_trgm函數(shù)

函數(shù)

描述

similarity ( text, text ) → real

返回一個(gè)數(shù)字指示兩個(gè)參數(shù)有多相似。該結(jié)果的范圍是 0(指示兩個(gè)字符串完全不相似)到 1(指示兩個(gè)字符串完全一樣)。

show_trgm ( text ) → text[]

返回一個(gè)給定字符串中所有的 trigram 組成的一個(gè)數(shù)組(實(shí)際上除了調(diào)試很少有用)。

word_similarity ( text, text ) → real

返回一個(gè)數(shù)字表示第一個(gè)字符串中的trigram集合與第二個(gè)字符串中trigram的有序集中任何連續(xù)部分的最大相似度。詳情請(qǐng)見下文的解釋。

strict_word_similarity ( text, text ) → real

word_similarity(text, text)相同,但是強(qiáng)制連續(xù)部分的邊界與詞邊界相匹配。 由于我們沒有跨詞的trigram,這個(gè)函數(shù)實(shí)際上返回第一個(gè)字符串和第二個(gè)字符串任意連續(xù)部分的相似度。

show_limit () → real

返回%操作符使用的當(dāng)前相似度閾值。例如,這設(shè)定兩個(gè)詞被認(rèn)為足夠相似時(shí), 它們之間應(yīng)滿足的最小相似度(已廢棄; 而是使用SHOW pg_trgm.similarity_threshold)。

set_limit ( real ) → real

設(shè)定%操作符使用的當(dāng)前相似度閾值。該閾值必須介于 0 和 1 之間(默認(rèn)為 0.3)。 返回傳遞進(jìn)來的同一個(gè)值(已廢棄; 而是使用SET pg_trgm.similarity_threshold)。


考慮下面的例子:

# SELECT word_similarity('word', 'two words');
 word_similarity
-----------------
             0.8
(1 row)

在第一個(gè)字符串中,trigram集合是{" w"," wo","wor","ord","rd "}。在第二個(gè)字符串中,trigram的有序集是{" t"," tw","two","wo "," w"," wo","wor","ord","rds","ds "}。在第二個(gè)字符串中最相似的trigram有序集的部分是{" w"," wo","wor","ord"},并且相似度是 0.8。

這個(gè)函數(shù)返回的值可以大概地理解為第一個(gè)字符串和第二個(gè)字符串任意子串的最大相似度。不過,這個(gè)函數(shù)不會(huì)對(duì)該部分的邊界加入填充。因此,除了失配的詞邊界之外,第二個(gè)字符串中存在的額外字符的數(shù)目沒有被考慮。

同時(shí),strict_word_similarity在第二個(gè)字符串中選擇一個(gè)由詞構(gòu)成的部分。 在上面的例子中,strict_word_similarity會(huì)選擇單個(gè)詞'words'形成的部分, 其trigram集合為{" w"," wo","wor","ord","rds","ds "}。

# SELECT strict_word_similarity('word', 'two words'), similarity('word', 'words'); strict_word_similarity | similarity ------------------------+------------ 0.571429 | 0.571429 (1 row)

因此,strict_word_similarity函數(shù)對(duì)于計(jì)算整個(gè)詞的相似度有用,而word_similarity更適合于計(jì)算詞的部分相似度。

表 F.25. pg_trgm操作符

操作符

描述

text % textboolean

如果參數(shù)具有超過pg_trgm.similarity_threshold設(shè)置的當(dāng)前相似度閾值的相似度,則返回true。

text <% textboolean

如果第一個(gè)參數(shù)中的trigram集合與第二個(gè)參數(shù)中有序trigram集合的一個(gè)連續(xù)部分之間的相似度超過 pg_trgm.word_similarity_threshold參數(shù)設(shè)置的當(dāng)前詞相似度閾值, 則返回true。

text %> textboolean

<%操作符的交換子。

text <<% textboolean

如果第二個(gè)參數(shù)有有序trigram集合的一個(gè)連續(xù)部分匹配詞邊界,并且其與第一個(gè)參數(shù)的trigram集合的相似度超過 pg_trgm.strict_word_similarity_threshold參數(shù)設(shè)置的當(dāng)前嚴(yán)格詞相似度閾值, 則返回true。

text %>> textboolean

<<%操作符的交換子。

text <-> textreal

返回參數(shù)之間的距離,即 1 減去similarity()值。

text <<-> textreal

返回參數(shù)之間的距離,它是 1 減去word_similarity()的值。

text <->> textreal

<<->操作符的交換子。

text <<<-> textreal

返回參數(shù)之間的距離,也就是1減去strict_word_similarity()的值。

text <->>> textreal

<<<->操作符的交換子。


F.31.3. GUC 參數(shù)

pg_trgm.similarity_threshold (real)

設(shè)置%操作符使用的當(dāng)前相似度閾值。該閾值必須位于 0 和 1 之間(默認(rèn)是 0.3)。

pg_trgm.word_similarity_threshold (real)

設(shè)置<%%>操作符使用的當(dāng)前詞相似度閾值。該閾值必須位于 0 和 1 之間(默認(rèn)是 0.6)。

pg_trgm.strict_word_similarity_threshold (real)

設(shè)置<<%%>>運(yùn)算符使用的當(dāng)前嚴(yán)格單詞相似性閾值。 閾值必須介于0和1之間(默認(rèn)值為0.5)。

F.31.4. 索引支持

pg_trgm模塊提供了 GiST 和 GIN 索引操作符類,這允許你在一個(gè)文本列上創(chuàng)建索引用于快速相似度搜索的目的。這些索引類型支持上述的相似度操作符,并且額外支持基于 trigram 的索引搜索用于LIKE、ILIKE、~~*查詢(這些索引不支持等值或簡(jiǎn)單比較操作符,因此你可能還需要一個(gè)常規(guī)的 B-樹索引)。

例子:

CREATE TABLE test_trgm (t text);
CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops);

CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);

gist_trgm_ops GiST opclass 將一組三元組近似為位圖簽名。 它的可選整數(shù)參數(shù)siglen 確定簽名長(zhǎng)度(以字節(jié)為單位)。 默認(rèn)長(zhǎng)度為 12 個(gè)字節(jié)。簽名長(zhǎng)度的有效值介于 1 到 2024 字節(jié)之間。 更長(zhǎng)的簽名導(dǎo)致更精確的搜索(掃描索引的一小部分和更少的堆頁(yè)面),但代價(jià)是更大的索引。

創(chuàng)建簽名長(zhǎng)度為 32 字節(jié)的此類索引的示例:

CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));

此時(shí),你將有一個(gè)t列上的索引,你可以用它進(jìn)行相似度搜索。一個(gè)典型的查詢是

SELECT t, similarity(t, 'word') AS sml
  FROM test_trgm
  WHERE t % 'word'
  ORDER BY sml DESC, t;

這將返回在文本列中與word足夠相似的所有值,按最佳匹配到最差匹配的方式排序。索引將被用來讓這種搜索變快,即使在一個(gè)非常大的數(shù)據(jù)集上。

上述查詢的一種變體是

SELECT t, t <-> 'word' AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

這能夠用 GiST 索引有效地實(shí)現(xiàn),但是用 GIN 索引無法做到。當(dāng)只想要少數(shù)最接近的匹配時(shí),這通常會(huì)比第一種形式更好。

也可以把一個(gè)t列上的索引用于詞相似度或者嚴(yán)格詞相似度。典型的查詢是:

SELECT t, word_similarity('word', t) AS sml
  FROM test_trgm
  WHERE 'word' <% t
  ORDER BY sml DESC, t;

SELECT t, strict_word_similarity('word', t) AS sml
  FROM test_trgm
  WHERE 'word' <<% t
  ORDER BY sml DESC, t;

這將返回文本列中符合條件的所有值:這些值在其對(duì)應(yīng)的有序trigram集中有一個(gè)連續(xù)部分與word的trigram集合足夠相似,這些值會(huì)按照最好匹配到最差匹配的順序排列。即便在非常大的數(shù)據(jù)集上,索引也將使得這一操作的速度夠快。

上述查詢可能的變體有:

SELECT t, 'word' <<-> t AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

SELECT t, 'word' <<<-> t AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

這可以用 GiST 索引很高效地實(shí)現(xiàn),但是用 GIN 索引不行。

PostgreSQL 9.1 中開始,這些索引類型也支持用于LIKEILIKE的索引搜索,例如

SELECT * FROM test_trgm WHERE t LIKE '%foo%bar';

該索引搜索通過從搜索字符串中抽取 trigram 并且在索引中查找它們來工作。搜索字符串中有更多 trigram,索引搜索的效率更高。不像基于 B-樹的搜索,搜索字符串不需要是左錨定的。

PostgreSQL 9.3 中開始,這些索引類型也支持用于正則表達(dá)式匹配(~~*操作符)的索引搜索,例如

SELECT * FROM test_trgm WHERE t ~ '(foo|bar)';

該索引搜索通過從正則表達(dá)式中抽取 trigram 并且在索引中查找它們來工作。正則表達(dá)式中能抽取出更多 trigram,索引搜索的效率更高。不像基于 B-樹的搜索,搜索字符串不需要是左錨定的。

對(duì)于LIKE和正則表達(dá)式搜索,記住沒有可抽取 trigram 的模式將退化成一個(gè)全索引掃描。

GiST 和 GIN 索引之間的選擇依賴于 GiST 和 GIN 的相對(duì)性能特性,這在其他地方討論。

F.31.5. 文本搜索集成

在與一個(gè)全文索引聯(lián)合使用時(shí),trigram 匹配是一種非常有用的工具。特別是它能有助于識(shí)別拼寫錯(cuò)誤的輸入詞,這些詞直接用全文搜索機(jī)制是不會(huì)被匹配的。

第一步是生成一個(gè)包含文檔中所有唯一詞的輔助表:

CREATE TABLE words AS SELECT word FROM
        ts_stat('SELECT to_tsvector(''simple'', bodytext) FROM documents');

其中documents是一個(gè)具有我們希望搜索的文本域bodytext的表。對(duì)to_tsvector函數(shù)使用simple配置而不是使用語言相關(guān)的配置的原因是,我們想要一個(gè)原始(沒有去掉詞根的)詞的列表。

接下來,在詞列上創(chuàng)建一個(gè) trigram 索引:

CREATE INDEX words_idx ON words USING GIN(word gin_trgm_ops);

現(xiàn)在,類似于前面例子的一個(gè)SELECT查詢可以被用來為用戶搜索術(shù)語中的拼寫不當(dāng)?shù)脑~建議拼寫。要求被選擇的詞也與拼寫不當(dāng)?shù)脑~具有相似的長(zhǎng)度是一種有用的額外測(cè)試。

注意

由于words表已經(jīng)被生成為一個(gè)單獨(dú)的、靜態(tài)的表,它將需要被定期地重新生成,這樣它能合理地與文檔集合保持一致。但是要求它完全與文檔集合同步通常是不必要的。

F.31.6. 參考

GiST 開發(fā)站點(diǎn) http://www.sai.msu.su/~megera/postgres/gist/

Tsearch2 開發(fā)站點(diǎn) http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/

 


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)