F.31. pg_trgm

pg_trgm模块提供用于决定基于 trigram 匹配的字母数字文本相似度的函数和操作符,以及支持快速搜索相似字符串的索引操作符类。

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

一个 trigram 是从一个字符串中取出的由三个连续字符组成的组。我们可以通过对两个字符串之间共享的 trigram 计数来度量它们的相似度。这种简单的思想已经成为在很多自然语言中度量词相似度的有效方法。

注意: 在从一个字符串中提取 trigram 时,pg_trgm会忽略非词字符(非字母数字)。在决定字符串中所含的 trigram 集合时,每一个词被认为具有两个空格前缀和一个空格后缀。例如,字符串"cat"中的 trigram 集合是: " c"" ca""cat"以及 "at "。 字符串"foo|bar"中的 trigram 集合是: " f"" fo""foo""oo "" b"" ba""bar"以及 "ar "

F.31.2. 函数和操作符

pg_trgm模块所提供的函数如表 F-25中所示,操作符则显示在表 F-26中。

表 F-25. pg_trgm 函数

函数返回值描述
similarity(text, text)real 返回一个数字指示两个参数有多相似。该结果的范围是 0(指示两个字符串完全不相似)到 1(指示两个字符串完全一样)。
show_trgm(text)text[] 返回一个给定字符串中所有的 trigram 组成的一个数组(实际上除了调试很少有用)。
word_similarity(text, text) real 返回一个数,它表示第一个字符串和第二个字符串中最相似的词有多相似。这个函数在第二个字符串中是搜索最相似的词而不是最相似的子串。结果的范围是零(表示两个字符串完全不相似)到一(表示第一个字符串和第二个字符串中的一个词相同)。
show_limit()real 返回%操作符使用的当前相似度阈值。例如,这设定两个词被认为足够相似时,它们之间应满足的最小相似度(已废弃)。
set_limit(real)real 设定%操作符使用的当前相似度阈值。该阈值必须介于 0 和 1 之间(默认为 0.3)。返回传递进来的同一个值(已废弃)。

表 F-26. pg_trgm 操作符

操作符返回值描述
text % textboolean 如果参数具有超过pg_trgm.similarity_threshold设置的当前相似度阈值的相似度,则返回true
text <% textboolean 如果函数的第一个参数在第二个参数中有一个相似词并且它们之间的相似度大于由pg_trgm.word_similarity_threshold参数所设置的当前词相似度阈值,这个函数返回true
text %> textboolean <%操作符的交换子。
text <-> textreal 返回参数之间的"距离",即 1 减去similarity()值。
text <<-> text real 返回参数之间的"距离",它是 1 减去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)。

F.31.4. 索引支持

pg_trgm模块提供了 GiST 和 GIN 索引操作符类,这允许你在一个文本列上创建索引用于快速相似度搜索的目的。这些索引类型支持上述的相似度操作符,并且额外支持基于 trigram 的索引搜索用于LIKEILIKE~~*查询(这些索引不支持等值或简单比较操作符,因此你可能还需要一个常规的 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;

这会返回该文本列中有一个词与word足够相似的所有值,按相似度从好到坏排列。这个索引将被用来在对超大数据集操作时加速操作。

上述查询的一个变体是:

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

这可以用 GiST 索引很高效地实现,但是用 GIN 索引不行。

PostgreSQL 9.1 中开始,这些索引类型也支持用于LIKEILIKE的索引搜索,例如

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

该索引搜索通过从搜索字符串中抽取 trigram 并且在索引中查找它们来工作。搜索字符串中有更多 trigram,索引搜索的效率更高。不像基于 B-树的搜索,搜索字符串不需要是左锚定的。

PostgreSQL 9.3 中开始,这些索引类型也支持用于正则表达式匹配(~~*操作符)的索引搜索,例如

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

该索引搜索通过从正则表达式中抽取 trigram 并且在索引中查找它们来工作。正则表达式中能抽取出更多 trigram,索引搜索的效率更高。不像基于 B-树的搜索,搜索字符串不需要是左锚定的。

对于LIKE和正则表达式搜索,记住没有可抽取 trigram 的模式将退化成一个全索引扫描。

GiST 和 GIN 索引之间的选择依赖于 GiST 和 GIN 的相对性能特性,这在其他地方讨论。

F.31.5. 文本搜索集成

在与一个全文索引联合使用时,trigram 匹配是一种非常有用的工具。特别是它能有助于识别拼写错误的输入词,这些词直接用全文搜索机制是不会被匹配的。

第一步是生成一个包含文档中所有唯一词的辅助表:

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

其中documents是一个具有我们希望搜索的文本域bodytext的表。对to_tsvector函数使用simple配置而不是使用语言相关的配置的原因是,我们想要一个原始(没有去掉词根的)词的列表。

接下来,在词列上创建一个 trigram 索引:

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

现在,类似于前面例子的一个SELECT查询可以被用来为用户搜索术语中的拼写不当的词建议拼写。要求被选择的词也与拼写不当的词具有相似的长度是一种有用的额外测试。

注意: 由于words表已经被生成为一个单独的、静态的表,它将需要被定期地重新生成,这样它能合理地与文档集合保持一致。但是要求它完全与文档集合同步通常是不必要的。

F.31.6. 参考

GiST 开发站点 http://www.sai.msu.su/~megera/postgres/gist/

Tsearch2 开发站点 http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/

F.31.7. 作者

Oleg Bartunov ,俄罗斯莫斯科大学

Teodor Sigaev ,俄罗斯莫斯科 Delta-Soft 有限责任公司

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

文档:Christopher Kings-Lynne

这个模块由俄罗斯莫斯科 Delta-Soft 有限责任公司赞助。