【问题标题】:Match a phrase ending in a prefix with full text search用全文搜索匹配以前缀结尾的短语
【发布时间】:2023-03-20 22:13:01
【问题描述】:

我正在寻找一种在 PostgreSQL 中使用 tsvector 来模拟 SELECT * FROM table WHERE attr LIKE '%text%' 之类的方法。

我在不使用字典的情况下创建了一个 tsvector 属性。现在,像 ...

这样的查询
SELECT title
FROM table
WHERE title_tsv @@ plainto_tsquery('ph:*');  

... 将返回所有标题,如“Physics”、“PHP”等。但是我如何创建一个查询来返回标题以“Zend Fram”开头的所有记录(例如应该返回“Zend Framework”) ')?

当然,我可以使用类似的东西:

SELECT title
FROM table
WHERE title_tsv @@ to_tsquery('zend')
AND   title_tsv @@ to_tsquery('fram:*');

但是,这似乎有点尴尬。

所以,问题是:有没有一种方法可以使用类似的方式来制定上面给出的查询:

SELECT title
FROM table
WHERE title_tsv @@ to_tsquery('zend fram:*');

【问题讨论】:

  • 短语搜索是您旧问题的答案吗?

标签: postgresql full-text-search pattern-matching tsvector


【解决方案1】:

Postgres 9.6 为全文搜索引入了短语搜索功能。所以现在可以了:

SELECT title
FROM  tbl
WHERE title_tsv @@ to_tsquery('zend <-> fram:*');

&lt;-&gt; being the FOLLOWED BY operator.

找到'foo Zend framework bar''Zend frames',但是not 'foo Zend没有framework bar '.

引用release notes for Postgres 9.6:

可以使用新的在 tsquery 输入中指定短语搜索查询 运营商 &lt;-&gt;&lt;N&gt;。前者意味着之前的词位和 在它必须按该顺序彼此相邻出现之后。后者 意味着它们必须是完全分开的N 词位。

为了获得最佳性能支持使用 GIN 索引的查询:

CREATE INDEX tbl_title_tsv_idx ON tbl USING GIN (title_tsv);

或者根本不将title_tsv 存储在表中(使其膨胀并使写入复杂化)。您可以改用表达式索引:

CREATE INDEX tbl_title_tsv_idx ON tbl USING GIN (to_tsvector('english', title));

您需要指定文本搜索配置(通常是特定于语言的)以使表达式不可变。并相应地调整查询:

...
WHERE to_tsvector('english', title) @@ to_tsquery('english', 'zend <-> fram:*');

【讨论】:

  • some cases 中,您可能希望保留 tsvector 字段(而不是使用表达式索引。)
  • @DharmaTurtle:非常有用的补充!
【解决方案2】:

不是一个很好的解决方案,但它应该可以完成工作:

psql=# SELECT regexp_replace(cast(plainto_tsquery('Zend Fram') as text), E'(\'\\w+\')', E'\\1:*', 'g') ;
   regexp_replace    
---------------------
 'zend':* & 'fram':*
(1 row)

可以这样使用:

psql=# SELECT title FROM table WHERE title_tsv(title) @@ to_tsquery(regexp_replace(cast(plainto_tsquery('Zend Fram') as text), E'(\'\\w+\')', E'\\1:*', 'g'));

这是如何工作的:

  1. 将纯 tsquery 转换为字符串:cast(plainto_tsquery('Zend Fram') as text)
  2. 使用正则表达式将:*前缀匹配器附加到每个搜索词:regexp_replace(..., E'(\'\\w+\')', E'\\1:*', 'g')
  3. 将其转换回非普通 tsquery。 to_tsquery(...)
  4. 并在搜索表达式中使用它SELECT title FROM table WHERE title_tsv(title) @@ ...

【讨论】:

  • 丑,但我喜欢!穷人的前缀搜索。可耻的 trigram GIN 索引不能直接应用于 tsvector 列
  • 或者,您可以将整个查询与to_tsquery(CONCAT(CAST(plainto_tsquery('Zend Fram') as text), ':*')) 匹配。它没有那么丑。
【解决方案3】:

在 Postgres 中有一种方法可以使用 trigrams 和 Gin/Gist 索引。在 Kristo Kaiv 的这篇文章中,有一个简单的例子,但有一些粗糙的边缘:Substring Search

【讨论】:

    【解决方案4】:
    SELECT title
    FROM table
    WHERE title_tsv @@ to_tsquery('zend') and
    title_tsv @@ to_tsquery('fram:*')  
    

    相当于:

    SELECT title
    FROM table
    WHERE title_tsv @@ to_tsquery('zend & fram:*')
    

    但当然也会发现“Zend 没有框架”。

    您当然可以在 tsquery 匹配之后表达一个正则表达式与 title 匹配,但是您必须使用 explain analyze 来确保它是在 tsquery 之后而不是之前执行的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-23
      • 2018-04-21
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多