如何在 PostgreSQL 全文搜索中搜索带连字符的单词?

use*_*231 12 postgresql full-text-search pattern-matching

我必须搜索带连字符的单词,例如“早安”、“晚安”等。

我的查询是:

select id, ts_headline(content,
                       to_tsquery('english','good-morning'),
                       'HighlightAll=true MaxFragments=100 FragmentDelimiter=$') 
from table 
where ts_content @@ to_tsquery('english','good-morning');
Run Code Online (Sandbox Code Playgroud)

执行此查询时,我还会分别获得“good”“morning”的结果。但我想要完全匹配的单词和片段。
(因为ts_content我使用相同的默认配置english来创建tsvector.)

如何在 PostgreSQL 全文搜索中搜索此类带连字符的单词?

Erw*_*ter 10

这里的关键词是短语搜索,它是在Postgres 9.6 中引入的。

使用tsqueryFOLLOWED BY 运算符<->相关<N>运算符之一。或者更好的是,使用该函数phraseto_tsquery()生成您的tsquery.
引用手册,它...

生成tsquery搜索短语,忽略标点符号

和:

phraseto_tsquery行为很像plainto_tsquery,除了它<->在幸存的单词之间插入(FOLLOWED BY) 运算符而不是&(AND) 运算符。此外,停止词不会被简单地丢弃,而是通过插入<N>运算符而不是<-> 运算符来解决。这个函数在搜索精确的词位序列时很有用,因为 FOLLOWED BY 操作符检查词位顺序而不仅仅是所有词位的存在。

您的查询将如下工作:

select id
     , ts_headline(content, phraseto_tsquery('english', 'good-morning')
                          , 'HighlightAll=true MaxFragments=100 FragmentDelimiter=$') 
from   tbl 
where  ts_content @@ phraseto_tsquery('english','good-morning');
Run Code Online (Sandbox Code Playgroud)

phraseto_tsquery('english', 'good-morning')生成这个tsquery

'good-morn' <-> 'good' <-> 'morn'
Run Code Online (Sandbox Code Playgroud)

由于“早上好”被标识为asciihword(带连字符的 ASCII 字),因此在成分之前添加了词干完整的字。手册:

解析器可能会从同一段文本中产生重叠的标记。例如,带连字符的单词将作为整个单词和每个组件报告:(后跟示例)

to_tsvector()基本上在另一端做同样的事情,所以一切都匹配。这允许带有连字符的细粒度选项。上面只找到带有连字符的“早上好”(或衍生出相同的变体)。要查找所有带有“good”后跟“morn”的字符串(或衍生为相同的变体),请使用phraseto_tsquery('english','good morning')生成此 tsquery:'good' <-> 'morn'

OTOH,您可以通过添加另一个过滤器来强制精确匹配,例如:

...
AND content ~* 'good-morning'  -- case insensitive regexp match
Run Code Online (Sandbox Code Playgroud)

或者:

...
AND content ILIKE '%good-morning%'
Run Code Online (Sandbox Code Playgroud)

对人眼来说似乎有点多余,但通过这种方式,您可以获得快速的全文索引支持 精确匹配。

后者大部分是等价的,但不同(较少)的字符在LIKE模式中具有特殊含义,可能需要转义。有关的:

演示运算符的示例<N>

phraseto_tsquery('english', 'Juliet and the Licks')生成这个tsquery

'juliet' <3> 'lick'
Run Code Online (Sandbox Code Playgroud)

<3>这意味着lick必须是juliet.之后的第三个词素。