PostgreSQL:全文搜索 - 如何搜索部分单词?

时间:2010-03-25 06:32:11

标签: sql postgresql full-text-search

在这里发布了一个关于我如何提高SQL搜索方法速度的问题,我被建议更新我的表以使用全文搜索。这就是我现在所做的,使用Gist索引来加快搜索速度。在一些“普通”查询中,我注意到了一个显着的增长,我很高兴。

但是,我在搜索部分单词时遇到困难。例如,我有几个包含单词Squire(454)的记录,我有几个包含Squirrel(173)的记录。现在,如果我搜索Squire它只返回454条记录,但我也希望它返回Squirrel记录。

我的查询看起来像这样

SELECT title 
FROM movies 
WHERE vectors @@ to_tsoquery('squire');

我以为我可以做to_tsquery('squire%')但这不起作用 如何让它搜索部分匹配?

另外,在我的数据库中,我有电影和其他只是电视节目的唱片。这些都是通过名称来区分,所以像“Munsters”是电视节目,而Munsters则是电影节目。我希望能够做的只是搜索电视节目和电影。关于如何实现这个目标的任何想法?

此致 Anthoni

6 个答案:

答案 0 :(得分:47)

尝试,

SELECT title FROM movies WHERE to_tsvector(title) @@ to_tsquery('squire:*')

这适用于PostgreSQL 8.4+

答案 1 :(得分:30)

Anthoni,

假设您打算仅使用ASCII编码(可能很难,我知道),一个非常可行的选项可能是Trigram(pg_trgm)模块:http://www.postgresql.org/docs/9.0/interactive/pgtrgm.html

Trigram使用内置的索引方法,如Gist和Gin。您必须进行的唯一修改是在定义索引时,指定gist_trgm_opsgin_trgm_ops的运算符类。

如果尚未安装contrib模块,在Ubuntu中就可以轻松地从shell运行以下命令:

# sudo apt-get install postgresql-contrib

使contrib模块可用后,必须将pg_trgm扩展名安装到相关数据库中。您可以通过在要安装模块的数据库上执行以下PostgreSQL查询来执行此操作:

CREATE EXTENSION pg_trgm;

安装pg_trgm扩展程序后,我们已经准备好了!

-- Create a test table.
CREATE TABLE test (my_column text)
-- Create a Trigram index.
CREATE INDEX test_my_colun_trgm_idx ON test USING gist (my_column gist_trgm_ops);
-- Add a couple records
INSERT INTO test (my_Column) VALUES ('First Entry'), ('Second Entry'), ('Third Entry')
-- Query using our new index --
SELECT my_column, similarity(my_column, 'Frist Entry') AS similarity FROM test WHERE my_column % 'Frist Entry' ORDER BY similarity DESC

答案 2 :(得分:5)

即使使用LIKE,你也无法从squire%获得“松鼠”,因为“松鼠”有两个'r'。要获得Squire和Squirrel,您可以运行以下查询:

SELECT title FROM movies WHERE vectors @@ to_tsquery('squire|squirrel');

要区分电影和电视节目,您应该在数据库中添加一列。然而,有很多方法可以给这只猫皮肤。您可以使用子查询强制postgres首先找到匹配'squire'和'squirrel'的电影,然后搜索该子集以查找以'“'开头的标题。可以创建索引以供{{ 1}}搜索。

如果没有探索其他索引的可能性,你也可以运行它们 - 搞乱它们以找到最快的:

LIKE '"%...'

SELECT title 
FROM (
   SELECT * 
   FROM movies 
   WHERE vectors @@ to_tsquery('squire|squirrel')
) t
WHERE title ILIKE '"%';

答案 3 :(得分:5)

@ alexander-mera解决方案效果很棒!

注意:还要确保将空格转换为+。例如,如果您要搜索squire knight

SELECT title FROM movies WHERE to_tsvector(title) @@ to_tsquery('squire+knight:*')

答案 4 :(得分:2)

对此的广泛解决方案是使用PG的ts_rewrite函数来设置适用于备用匹配的别名表(请参阅Query Rewriting)。这包括与您相同的案例,同时还处理完全不同的案例,例如搜索tree rat并获取squirrel的结果等。

该链接的完整详细信息和说明,但其要点是您可以设置包含2个ts_query列的别名表,并将该表的查询传递给您的搜索,如下所示:

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');

导致最终查询看起来更像:

WHERE vectors @@ ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases')

这类似于PG中的词库设置,但每次添加内容时都不需要完整的重新索引。当你遇到很少的拼写变化和“当我搜索这个时我希望这样的结果”的情况下,很容易将它们快速地添加到表中。您可以向该表添加更多列,只要基于ts_rewrite的查询返回2个预期to_tsquery列。

当您深入了解该文档时,您还会看到性能调优的建议示例。在使用trigram进行纯粹速度和使用向量/查询/重写进行稳健性之间存在平衡。

答案 5 :(得分:0)

可能有用的一件事就是将你要搜索的单词分成更小的部分。所以你可以找到有squi或quir或squire等的东西......我不确定它会有多高效,但它可能有所帮助。

当您搜索电影或电影时,您可以尝试将文字放在单引号中。所以它可能是'show'或'show“'。我认为这也可行。