本节介绍与文本搜索相关的其他有用函数和运算符。
第 12.3.1 节介绍了如何将原始文本文档转换为 tsvector 值。PostgreSQL 还提供了函数和运算符,可用于操作已为 tsvector 形式的文档。
tsvector || tsvectortsvector 连接运算符返回一个向量,该向量结合了作为参数提供的两个向量的词元和位置信息。连接过程中保留了位置和权重标签。右侧向量中的位置会根据左侧向量中提到的最大位置进行偏移,因此结果在很大程度上等同于对两个原始文档字符串的连接执行 to_tsvector 的结果。(等价并非精确,因为从左侧参数末尾删除的任何停用词都不会影响结果,而如果在文本连接中使用,它们会影响右侧参数中词元的位置。)
使用向量形式进行连接而不是在应用 to_tsvector 之前连接文本的一个优点是,您可以使用不同的配置来解析文档的不同部分。此外,因为 setweight 函数以相同的方式标记给定向量的所有词元,所以如果要使用不同的权重标记文档的不同部分,则必须在连接之前解析文本并执行 setweight。
setweight(vector tsvector, weight "char") returns tsvectorsetweight 返回输入向量的副本,其中每个位置都用指定的 weight 标记,可以是 A、B、C 或 D。(D 是新向量的默认值,因此在输出中不显示。)连接向量时会保留这些标签,从而允许通过排名函数为文档的不同部分分配不同的权重。
请注意,权重标签适用于位置,而不是词元。如果输入向量已剥离位置信息,则 setweight 不执行任何操作。
length(vector tsvector) returns integer返回向量中存储的词元数。
strip(vector tsvector) returns tsvector返回一个列出与给定向量相同词元的向量,但不包含任何位置或权重信息。结果通常比未剥离的向量小得多,但用处也较少。相关性排名在剥离的向量上不如在未剥离的向量上效果好。此外,<->(后跟)tsquery 运算符永远不会匹配剥离的输入,因为它无法确定词元出现之间的距离。
文本搜索函数列表可在表 9.43 中找到。
第 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 ]) returns tsquery返回一个查询,该查询搜索第一个给定查询的匹配,然后在 distance 个词元的距离处搜索第二个给定查询的匹配,使用 < N>tsquery 运算符。例如
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
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 仅应用单个重写规则: wherever target appears in query, it is replaced by 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.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
创建此触发器后,对 title 或 body 的任何更改都将自动反映到 tsv 中,而无需应用程序担心。
第一个触发器参数必须是将被更新的 tsvector 列的名称。第二个参数指定用于执行转换的文本搜索配置。对于 tsvector_update_trigger,配置名称仅作为第二个触发器参数给出。它必须像上面那样使用模式限定,以便触发器行为不会因 search_path 的更改而改变。对于 tsvector_update_trigger_column,第二个触发器参数是另一个表列的名称,该列必须是 regconfig 类型。这允许按行选择配置。其余参数是文本列(text、varchar 或 char 类型)的名称。这些将按照给定的顺序包含在文档中。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 更改的影响。未能这样做很可能会导致问题,例如转储和恢复后搜索结果发生变化。
函数 ts_stat 有助于检查您的配置和查找停用词候选。
ts_stat(sqlquerytext, [weightstext, ] OUTwordtext, OUTndocinteger, OUTnentryinteger) returnssetof 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;
相同,但仅计算权重为 A 或 B 的单词出现次数。
SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;
如果您在文档中看到任何不正确、与您对特定功能的经验不符或需要进一步澄清的内容,请使用此表格报告文档问题。