问题报告 纠错本页面

12.4. 附加功能

本节描述了连接文本搜索中有用的附加功能和操作符。

12.4.1. 操作文档

第 12.3.1 节显示了原始文本文档如何转换成tsvector值。 PostgreSQL也提供用于操作已经在tsvector形式中的文档的函数和操作符。

tsvector || tsvector

tsvector连接操作符返回一个连接词的向量,以及作为参数给定的2个向量的位置信息。 在连接期间重新获得位置和权重标签。出现在右边向量位置通过左边向量提到的最大位置相抵消, 因此这个结果几乎等同于2个原始文档字符串连接中执行to_tsvector的结果。(这个等价是不准确的, 因为任何从左边参数中删除的屏蔽词不会影响结果,然而,如果使用文本连接,它们影响右边参数词的位置)。

使用级联中的向量形式而不是在应用to_tsvector之前连接文本的一个优势是, 你可以使用不同的配置解析文档的不同部分。同时,由于setweight函数标记所有相同方式给定向量的词汇, 解析文本是必要的,并且如果你想用不同的权重标记文档不同部分,连接前做setweight

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

setweight 返回一个输入向量的拷贝,其中每一个位置用给定的weight, A, B, C或者 D之一进行标记。(D是缺省新向量,因此不显示在输出上。)当向量连接时,保留这些标签, 允许一个文档的不同部分的词通过不同相关函数加权。

注意权重标签适用于位置,不是词汇。如果输入向量已经被剥夺了位置,则setweight不做任何事情。

length(vector tsvector) returns integer

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

strip(vector tsvector) returns tsvector

返回一个向量,其中列出了给定向量的同一词,但它缺乏任何位置和权重信息。 虽然为相关性排序返回的向量比一个未拆分向量用处少,它通常会小得多。

12.4.2. 处理查询

第 12.3.2 节显示了原始文本查询如何转换成tsquery值。 PostgreSQL也提供了函数和操作符用于处理已存在tsquery形式中的查询

tsquery && tsquery

返回两个给定查询的与组合。

tsquery || tsquery

返回两个给定查询的或组合。

!! tsquery

返回给定查询的反面(非)。

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
-----------

12.4.2.1. 查询重写

函数族ts_rewrite搜索一个特定的目标查询事件tsquery,和替换每个替代子查询。 实际上这个操作是一个子字符串替换的tsquery-特定版本。目标和替换组合可以被认为是一个查询重写规则。 一组这样的重写规则可以是一个强大的搜索帮助。例如,你可以使用同义词扩大搜索(例如,new york, big apple, nyc, gotham)或缩小搜索一些热点问题的直接用户。在这些特性和同义词词典之间功能上有一些重叠(节第 12.6.4 节)。然而, 你可以在不重建索引情况下即时修改重写规则,而更新词库需要重建索引才能有效。

ts_rewrite (query tsquery, target tsquery, substitute tsquery) returns tsquery

ts_rewrite的这种形式只适用于一个单一的重写规则:无论出现在query的什么地方,target通过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的这种形式接受起始查询和SQL select命令,这是作为一个文本字符串。 select必须产生两列tsquery类型。select结果的每一行,出现的第一个字段的值(目标) 都被当前的query值中的第二个字段值(替代)。比如:

注意,当多个重写规则适用于这种方式时,应用的顺序非常重要; 因此在实践中你将需要源查询为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. 自动更新的触发器

当使用单独的列存储文档的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 [, ... ])

这些触发器函数自动计算来自一个或多个文本字段的tsvector列,在CREATE TRIGGER命令指定的参数控制下。 使用的例子是:

CREATE TABLE messages (
    title       text,
    body        text,
    tsv         tsvector
);

CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON messages FOR EACH ROW EXECUTE PROCEDURE
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

创建触发器,在title或者body中的任何改变都会自动反映到tsv中,而不必担心它的应用。

第一个触发器参数必须是被更新的tsvector字段名。第二个参数指定要进行转换的文本搜索配置。 为tsvector_update_trigger,配置的名称仅仅是作为第二个触发器参数。它必须是如上所示的模式匹配, 因此触发器的行为在search_path中不会改变。为tsvector_update_trigger_column, 第二个触发器参数是另一个表列的名称,它的类型必须是regconfig。这允许每行选择进行配置。 剩余的参数(s)是文本列的名称(键入text, varchar或者char)。这些将在给定的顺序中提供文档。 空值将被忽略(但其他列仍将被索引)。

这些内置触发器的限制是它们一致对待所有输入列。为了处理不同列—比如, 为权重不同主体的标题—它有必要编写一个自定义触发器。 这是使用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 PROCEDURE 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数据的各个不同的语义(词)的统计。返回的列:

如果提供weights ,仅仅计算这些权重之一。

例如,在一个文档集合中查找十个最常用的单词:

SELECT * FROM ts_stat('SELECT vector FROM apod')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;

同样的,但是只计算权重weight A或者B的单词:

SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;