Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

12.4. 附加功能 #

12.4.1. 操作文档
12.4.2. 操作查询
12.4.3. 自动更新的触发器
12.4.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 中提供了 tsvector 相关函数的完整列表。

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

返回一个查询,用于搜索与第一个给定查询匹配且在距离精确为 distance 词素的位置匹配第二个给定查询的内容,使用 <N> tsquery 运算符。例如

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 只应用一个重写规则:无论 target 出现于 query 的何处,都被 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;