受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

12.4. 附加特性 #

本节介绍一些在文本搜索中很有用的附加函数和操作符。

12.4.1. 操纵文档 #

Section 12.3.1 展示了原始文本文档如何被转换成 tsvector 值。PostgreSQL 也提供了用于操纵已经处于 tsvector 形式文档的函数和操作符。

tsvector || tsvector #

tsvector 连接操作符返回一个向量,它结合了两个参数向量中的词位和位置信息。位置和权重标签在连接过程中会被保留。右侧向量中的位置会按左侧向量中出现的最大位置做偏移,因此结果几乎等同于对两个原始文档字符串连接后的结果执行 to_tsvector。(这种等价并不完全成立,因为从左侧参数末尾移除的停用词不会影响结果;而如果直接做文本连接,它们本来会影响右侧参数中词位的位置。)

相比先连接文本再应用 to_tsvector,直接连接向量的一个优点是,你可以用不同配置来解析文档的不同部分。此外,由于 setweight 会以相同方式标记给定向量中的全部词位,如果希望给文档不同部分打上不同权重,就必须先解析文本并调用 setweight,然后再做连接。

setweight(vector tsvector, weight "char") returns tsvector #

setweight 返回输入向量的一个副本,其中每个位置都被标注为给定的 weightABCDD 是新向量的默认值,因此不会在输出中显示)。这些标签在向量连接时会被保留,从而允许排名函数对来自文档不同部分的词赋予不同权重。

注意权重标签是应用到位置而不是词位。如果输入向量已经被剥离了位置,则setweight什么也不会做。

length(vector tsvector) returns integer #

返回存储在向量中的词位数。

strip(vector tsvector) returns tsvector #

返回一个向量,其中列出与给定向量相同的词位,但不包含任何位置或权重信息。其结果通常比未剥离的向量小得多,但也没那么有用。相关度排名在已剥离向量上的效果不如未剥离向量。此外,<->(FOLLOWED BY)tsquery 操作符永远不会匹配已剥离的输入,因为它无法确定词位出现之间的距离。

Table 9.43中有tsvector相关函数的完整列表。

12.4.2. 操纵查询 #

Section 12.3.2 展示了原始文本查询如何被转换成 tsquery 值。PostgreSQL 也提供了用于操纵已经处于 tsquery 形式查询的函数和操作符。

tsquery && tsquery #

返回用 AND 结合的两个给定查询。

tsquery || tsquery #

返回用 OR 结合的两个给定查询。

!! tsquery #

返回给定查询的否定(NOT)。

tsquery <-> tsquery #

返回一个查询,它使用 <->(FOLLOWED BY)tsquery 操作符,搜索第一个给定查询的匹配后紧跟着第二个给定查询的匹配。例如:

SELECT to_tsquery('fat') <-> to_tsquery('cat | rat');
          ?column?
----------------------------
 'fat' <-> ( 'cat' | 'rat' )
tsquery_phrase(query1 tsquery, query2 tsquery [, distance integer ]) returns tsquery #

返回一个查询,它使用 <N> tsquery 操作符,搜索第一个给定查询的匹配,并在距离正好为 distance 个词位处搜索第二个给定查询的匹配。例如:

SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10);
  tsquery_phrase
------------------
 'fat' <10> 'cat'
numnode(query tsquery) returns 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) returns 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),或者收窄搜索,把用户引向某个热门主题。该特性与分类词典(Section 12.6.4)在功能上有一定重叠。不过,重写规则可以随时修改而无需重建索引,而更新分类词典则必须重建索引后才能生效。

ts_rewrite (query tsquery, target tsquery, substitute tsquery) returns 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) returns 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. 用于自动更新的触发器 #

Note

本节描述的方法,已经被使用存储生成列的做法所取代,如 Section 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 是一个文本值,其中包含一条必须返回单一 tsvector 列的 SQL 查询。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;

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。