支持的版本:当前 (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") 返回 tsvector

setweight 返回输入向量的副本,其中每个位置都已使用给定的 weight 标记,可以是 ABCD。(D 是新向量的默认值,因此不会在输出中显示。)当向量连接时,这些标签会被保留,允许通过排名函数对文档不同部分的单词进行不同的加权。

请注意,权重标签适用于位置,而不是词素。如果输入向量已去除位置,则 setweight 不会执行任何操作。

length(vector tsvector) 返回 integer

返回向量中存储的词素数量。

strip(vector tsvector) 返回 tsvector

返回一个向量,其中列出与给定向量相同的词素,但缺少任何位置或权重信息。结果通常比未剥离的向量小得多,但它的用处也较少。相关性排名在剥离的向量上不如在未剥离的向量上效果好。此外,<->(紧随其后)tsquery 运算符永远不会匹配剥离的输入,因为它无法确定词素出现之间的距离。

有关 tsvector 相关函数的完整列表,请参阅表 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 ]) 返回 tsquery

返回一个查询,该查询使用 <N> tsquery 运算符搜索与第一个给定查询匹配的内容,后跟与第二个给定查询匹配的内容,距离正好为 distance 个词素。例如

SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10);
  tsquery_phrase
------------------
 'fat' <10> 'cat'
numnode(query tsquery) 返回 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) 返回 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) 返回 tsquery

此形式的 ts_rewrite 仅应用一个重写规则:targetquery 中出现的位置都将被 substitute 替换。例如

SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery);
 ts_rewrite
------------
 'b' & 'c'
ts_rewrite (query tsquery, select text) 返回 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;

提交更正

如果您在文档中看到任何不正确、与您使用特定功能的经验不符或需要进一步澄清的地方,请使用此表格报告文档问题。