Postgresql:如何为 tsquery 设置权重

her*_*ver 3 postgresql full-text-search

如何设置权重tsquery?我需要为tsqueryplainto_tsquery.

是否可以?类似的东西setweight(plainto_tsquery(''), 'A'),但它仅适用于tsvector.

小智 5

我也有这个问题。我的用例是大文档,很多部分,我希望提供一个“仅搜索标题文本”的选项。(标题的权重为 A,分散在整个文档中;其他部分的权重为 B、C 或 D,具体取决于它们出现的位置。)

这里有两个应该有帮助的解决方案。

方案一:tsquery的setweight函数

该函数将 tsquery 转换为文本,应用正则表达式来设置权重,然后转换回 tsquery。

CREATE FUNCTION setweight(query tsquery, weights text) RETURNS tsquery AS $$
    SELECT regexp_replace(
                query::text, 
                '(?<=[^ !])'':?(\*?)A?B?C?D?', ''':\1'||weights, 
                'g'
            )::tsquery;
$$ LANGUAGE SQL IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)

例子:

select setweight( plainto_tsquery('fat cats and rats'), 'A' );
-- 'fat':A & 'cat':A & 'rat':A

select setweight( phraseto_tsquery('fat cats and rats'), 'A' );
-- 'fat':A <-> 'cat':A <2> 'rat':A

select setweight( to_tsquery('fat & (cat:A & rat) & !dog:*CD'), 'BC' );
-- 'fat':BC & 'cat':BC & 'rat':BC & !'dog':*BC
Run Code Online (Sandbox Code Playgroud)

方案二:基于过滤后的tsvector的功能索引

首先在要搜索的全文列上创建其他索引。

例如

CREATE INDEX fulltext_idx
    ON your_table USING gin
    (fulltext)

CREATE INDEX fulltext_idx_A
    ON your_table USING gin
    (ts_filter(fulltext, '{a}'))

CREATE INDEX fulltext_idx_AB
    ON your_table USING gin
    (ts_filter(fulltext, '{a,b}'))
Run Code Online (Sandbox Code Playgroud)

对于您需要的任何重量组合。

然后,在搜索时,使用过滤后的表达式。例如:

SELECT * 
FROM your_table
WHERE ts_filter(fulltext, '{a}') @@ plainto_tsquery('your query')
Run Code Online (Sandbox Code Playgroud)

搜索应该在索引表达式上进行。

讨论

解决方案 1 为您提供了您正在寻找的函数,但加权查询的问题在于,尽管 postgres 将使用索引来查找候选匹配项,但它仍然需要拉回每个文档以检查权重。

就我而言,仅按标题搜索时,解决方案 2 似乎提供了更好的性能。标题中的文本(权重A)使用的词汇量比整个文档中的要小得多,因此fulltext_idx_A比fulltext_idx小得多,匹配后不需要重新检查结果。

对于您自己的情况,性能将完全取决于您自己的文档结构和查询的性质,因此请使用“解释分析”进行测试以选择更好的解决方案。考虑到票的年龄,我想你已经解决了这个问题:-)

注意:ts_filter()phraseto_tsquery()来自 Postgres 9.6。