↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3
历史版本。 PostgreSQL 11 已结束支持。 2023-11-09. 请参阅 当前版本手册.

F.31. pg_trgm #

pg_trgm 模块提供函数和操作符,用于基于三字符组匹配确定字母数字文本的相似度,同时还提供支持快速搜索相似字符串的索引操作符类。

F.31.1. 三字符组(Trigram 或 Trigraph)概念

三字符组是一组从字符串中取出的三个连续字符。我们可以通过统计两个字符串共享的三字符组数量来度量它们的相似度。这个简单的思想在度量许多自然语言中词的相似度时都非常有效。

注意

从字符串中提取三字符组时,pg_trgm 会忽略非词字符(即非字母数字字符)。在确定字符串所包含的三字符组集合时,认为每个词前面都有两个空格,后面都有一个空格。例如,字符串“cat”的三字符组集合是“ c”、“ ca”、“cat”和“at ”。字符串“foo|bar”的三字符组集合是“ f”、“ fo”、“foo”、“oo ”、“ b”、“ ba”、“bar”和“ar ”。

F.31.2. 函数和操作符

pg_trgm 模块提供的函数列在表 F.24 中,操作符列在表 F.25 中。

表 F.24. pg_trgm 函数

函数返回值 描述
similarity(text, text)real返回一个表示两个参数有多相似的数值。结果范围从 0(表示两个字符串完全不同)到 1(表示两个字符串完全相同)。
show_trgm(text)text[] 返回由给定字符串中所有三字符组构成的数组。(实际应用中,除了调试之外很少有用。)
word_similarity(text, text) real 返回一个数值,表示第一个字符串中的三字符组集合与第二个字符串中的有序三字符组集合中任意连续区段之间的最大相似度。详见下文说明。
strict_word_similarity(text, text) real与 word_similarity(text, text) 相同,但强制范围边界与单词边界相匹配。由于不存在跨单词的三字符组,该函数实际返回第一个字符串与第二个字符串中任意连续单词范围之间的最大相似度。
show_limit()real返回% 操作符当前使用的相似度阈值。它设置两个单词被视为足够相似、例如可以看作彼此的拼写错误所需的最小相似度(已弃用)。
set_limit(real)real设置% 操作符当前使用的相似度阈值。阈值必须介于 0 和 1 之间(默认值为 0.3)。返回传入的相同值(已弃用)。

考虑以下示例:

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

在第一个字符串中,三字符组集合为{" w"," wo","wor","ord","rd "}。在第二个字符串中,有序三字符组集合为{" t"," tw","two","wo "," w"," wo","wor","ord","rds","ds "}。第二个字符串的有序三字符组集合中最相似的连续区段是{" w"," wo","wor","ord"},相似度为 0.8。

这个函数返回的值大致可以理解为第一个字符串与第二个字符串任意子串之间的最大相似度。不过,该函数不会在这个区段的边界处添加填充。因此,除了词边界不匹配的情况外,第二个字符串中额外存在的字符数不会被考虑在内。

同时,strict_word_similarity(text, text) 会在第二个字符串中选择一个由完整单词组成的连续区段。在上面的示例中,strict_word_similarity(text, text) 会选择仅包含单个单词的区段,这个单词是'words',其三字符组集合为{" 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(text, text) 适合查找与整个词的相似度,而 word_similarity(text, text) 更适合查找与词的一部分的相似度。

表 F.25. pg_trgm 操作符

操作符返回值 描述
text % textboolean 如果参数之间的相似度大于 pg_trgm.similarity_threshold 设置的当前相似度阈值,则返回 true。
text <% textboolean 如果第一个参数中的三字符组集合与第二个参数中的有序三字符组集合某个连续区段之间的相似度大于 pg_trgm.word_similarity_threshold 参数设置的当前词相似度阈值,则返回 true。
text %> textboolean <% 操作符的交换子。
text <<% textboolean 如果第二个参数中存在一个与词边界一致的有序三字符组集合连续区段,且它与第一个参数三字符组集合的相似度大于 pg_trgm.strict_word_similarity_threshold 参数设置的当前严格词相似度阈值,则返回 true。
text %>> textboolean <<% 操作符的交换子。
text <-> textreal返回参数之间的“距离”,即 1 减去 similarity() 的值。
text <<-> textreal返回参数之间的“距离”,即 1 减去 word_similarity() 的值。
text <->> textreal <<-> 操作符的交换子。
text <<<-> text real返回参数之间的“距离”,即 1 减去 strict_word_similarity() 的值。
text <->>> text real <<<-> 操作符的交换子。

F.31.3. GUC 参数

pg_trgm.similarity_threshold (real) #

设置% 操作符使用的当前相似度阈值。该阈值必须介于 0 和 1 之间(默认值为 0.3)。

pg_trgm.word_similarity_threshold(real)#

设置<% 和%> 操作符使用的当前词相似度阈值。该阈值必须介于 0 和 1 之间(默认值为 0.6)。

pg_trgm.strict_word_similarity_threshold(real)#

设置<<% 和%>> 操作符使用的当前严格词相似度阈值。该阈值必须介于 0 和 1 之间(默认值为 0.5)。

F.31.4. 索引支持

pg_trgm 模块提供 GiST 和 GIN 索引操作符类,允许你为文本列创建索引,以实现非常快速的相似度搜索。这些索引类型支持上述相似度操作符,还支持对 LIKE、ILIKE、~和~* 查询执行基于三字符组的索引搜索。(这些索引不支持等值操作符或简单比较操作符,因此你可能还需要一个常规 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);

此时,你已经在 t 列上建立了一个可用于相似度搜索的索引。典型查询如下:

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

这会返回文本列中所有与 word 足够相似的值,并按从最佳匹配到最差匹配的顺序排序。即使在非常大的数据集上,索引也会让这一操作保持高效。

上述查询的一个变体是

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

GiST 索引可以相当高效地实现这一点,但 GIN 索引不能。当只需要少量最接近的匹配项时,它通常会优于第一种写法。

还可以把 t 列上的索引用于词相似度或严格词相似度搜索。典型查询如下:

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;

这会返回文本列中所有满足以下条件的值:在其对应的有序三字符组集合中,存在一个连续区段与 word 的三字符组集合足够相似。结果按从最佳匹配到最差匹配的顺序排序。即使在非常大的数据集上,索引也会让这一操作保持高效。

上述查询的可能变体还有:

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 索引可以相当高效地实现这一点,但 GIN 索引不能。

从 PostgreSQL 9.1 起,这些索引类型还支持 LIKE 和 ILIKE 的索引搜索,例如

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

索引搜索的工作方式是从搜索字符串中提取三字符组,然后在索引中查找这些三字符组。搜索字符串中包含的三字符组越多,索引搜索就越有效。与基于 B-树的搜索不同,搜索字符串不需要在左端锚定。

从 PostgreSQL 9.3 起,这些索引类型还支持正则表达式匹配(~和~* 操作符)的索引搜索,例如

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

索引搜索的工作方式是从正则表达式中提取三字符组,然后在索引中查找这些三字符组。能从正则表达式中提取出的三字符组越多,索引搜索就越有效。与基于 B-树的搜索不同,搜索字符串不需要在左端锚定。

对于 LIKE 和正则表达式搜索,都要记住:无法提取出三字符组的模式会退化为全索引扫描。

GiST 和 GIN 索引之间如何取舍,取决于二者各自的相对性能特征;相关讨论见其他章节。

F.31.5. 全文检索集成

与全文索引结合使用时,三字符组匹配是非常有用的工具。尤其是,它有助于识别那些因拼写错误而无法被全文检索机制直接匹配的输入词。

第一步是生成一个辅助表,其中包含文档中的所有不重复的词:

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

其中 documents 是一个表,包含我们希望搜索的文本字段 bodytext。之所以对 to_tsvector 函数使用 simple 配置,而不是使用特定语言的配置,是因为我们需要原始的(未经词干提取的)词列表。

接下来,在词列上创建一个三字符组索引:

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

现在,可以使用与前面示例类似的 SELECT 查询,为用户搜索词中拼错的单词提供拼写建议。一个有用的附加测试是要求选出的词长度也与该拼错单词相近。

注意

由于 words 表是作为一张独立的静态表生成的,因此需要定期重新生成,以便与文档集合保持大致同步。通常没有必要让它始终保持精确同步。

F.31.7. 作者

Oleg Bartunov ,俄罗斯莫斯科,莫斯科大学

Teodor Sigaev ,俄罗斯莫斯科,Delta-Soft Ltd.

Alexander Korotkov ,俄罗斯莫斯科,Postgres Professional

文档:Christopher Kings-Lynne

该模块由俄罗斯莫斯科的 Delta-Soft Ltd. 赞助。

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.