用于比较列中每个单词开头的索引
Index for comparing to beginning of every word in a column
所以我有一个table
id | name | gender
---+-----------------+-------
0 | Markus Meskanen | M
1 | Jack Jackson | M
2 | Jane Jackson | F
并且我创建了一个索引
CREATE INDEX people_name_idx ON people (LOWER(name));
然后我用
查询
SELECT * FROM people WHERE name LIKE LOWER('Jack%');
其中 %(name)s
是用户的输入。但是,它现在只匹配整列的开头,但我希望它匹配任何单词的开头。我不想使用 '%Jack%'
,因为它也会导致单词中间的无效结果。
有没有办法创建一个索引,让每个单词单独占一行?
编辑: 如果名称像 'Michael Jackson's First Son Bob'
这样长,它应该匹配到任何单词的开头,即 Mich
将匹配到Michael
和 Fir
会匹配到 First
但 ackson
不会匹配任何东西,因为它不是从头开始的。
编辑 2: 我们有 300 万行,所以性能是一个问题,因此我主要查看索引。
如果你知道单词是space分开的,你可以
SELECT * FROM people WHERE name LIKE LOWER('Jack%') or name LIKE LOWER(' Jack%') ;
要获得更多控制,您可以将 RegEx 与 MySQl
结合使用
Postgres 有两种索引类型来帮助进行全文搜索:GIN 和 GIST 索引(我认为 GIN 是更常用的一种)。
documentation. There is more extensive documentation for each index class, as well as plenty of blogs on the subject (here is one and here中有索引的简要概述。
这些可以加快您尝试进行的搜索。
您可以使用 Regex expressions 查找名称中的文本:
create table ci(id int, name text);
insert into ci values
(1, 'John McEnroe Blackbird Petrus'),
(2, 'Michael Jackson and Blade');
select id, name
from ci
where name ~ 'Pe+'
;
Returns:
1 John McEnroe Blackbird Petrus
或者可以使用类似的东西where substring(name, <regex exp>) is not null
pg_trgm
module 完全符合您的要求。
您需要创建:
CREATE INDEX people_name_idx ON people USING GIST (name gist_trgm_ops);
或者:
CREATE INDEX people_name_idx ON people USING GIN (name gin_trgm_ops);
之后,这些查询可以使用上述索引之一:
SELECT * FROM people WHERE name ILIKE '%Jack%';
SELECT * FROM people WHERE name ~* '\mJack';
因为 , full text search 也可以通过前缀匹配进行搜索。但是 FTS 并不是为了高效地做到这一点而设计的,它最擅长匹配词素。不过,如果您想获得最佳性能,我建议您也尝试一下并进行测量。在 FTS 中,您的查询看起来像这样:
SELECT * FROM people WHERE to_tsvector('english', name) @@ to_tsquery('english', 'Jack:*');
注意:但是,如果您的查询过滤器 (Jack
) 来自用户输入,则上述两个查询都需要一些保护(即在 ILIKE
一个你需要转义 %
和 _
字符,在正则表达式中你需要转义更多,而在 FTS 一个中,你需要用一些解析器解析查询 &生成有效的 FTS' tsquery
查询,因为如果 to_tsquery()
的参数无效,则会给您一个错误。在 plainto_tsquery()
中,您不能使用前缀匹配查询)。
注 2:带有 name ~* '\mJack'
的正则表达式变体最适用于英文名称。如果你想使用整个范围的 unicode(即你想使用字符,比如 æ
),你需要一个稍微不同的模式。类似于:
SELECT * FROM people WHERE name ~* '(^|\s|,)Jack';
这将适用于大多数名称,此外,对于一些旧名称,这也将像真正的前缀匹配一样工作,例如 O'Brian
。
所以我有一个table
id | name | gender
---+-----------------+-------
0 | Markus Meskanen | M
1 | Jack Jackson | M
2 | Jane Jackson | F
并且我创建了一个索引
CREATE INDEX people_name_idx ON people (LOWER(name));
然后我用
查询SELECT * FROM people WHERE name LIKE LOWER('Jack%');
其中 %(name)s
是用户的输入。但是,它现在只匹配整列的开头,但我希望它匹配任何单词的开头。我不想使用 '%Jack%'
,因为它也会导致单词中间的无效结果。
有没有办法创建一个索引,让每个单词单独占一行?
编辑: 如果名称像 'Michael Jackson's First Son Bob'
这样长,它应该匹配到任何单词的开头,即 Mich
将匹配到Michael
和 Fir
会匹配到 First
但 ackson
不会匹配任何东西,因为它不是从头开始的。
编辑 2: 我们有 300 万行,所以性能是一个问题,因此我主要查看索引。
如果你知道单词是space分开的,你可以
SELECT * FROM people WHERE name LIKE LOWER('Jack%') or name LIKE LOWER(' Jack%') ;
要获得更多控制,您可以将 RegEx 与 MySQl
结合使用Postgres 有两种索引类型来帮助进行全文搜索:GIN 和 GIST 索引(我认为 GIN 是更常用的一种)。
documentation. There is more extensive documentation for each index class, as well as plenty of blogs on the subject (here is one and here中有索引的简要概述。
这些可以加快您尝试进行的搜索。
您可以使用 Regex expressions 查找名称中的文本:
create table ci(id int, name text);
insert into ci values
(1, 'John McEnroe Blackbird Petrus'),
(2, 'Michael Jackson and Blade');
select id, name
from ci
where name ~ 'Pe+'
;
Returns:
1 John McEnroe Blackbird Petrus
或者可以使用类似的东西where substring(name, <regex exp>) is not null
pg_trgm
module 完全符合您的要求。
您需要创建:
CREATE INDEX people_name_idx ON people USING GIST (name gist_trgm_ops);
或者:
CREATE INDEX people_name_idx ON people USING GIN (name gin_trgm_ops);
之后,这些查询可以使用上述索引之一:
SELECT * FROM people WHERE name ILIKE '%Jack%';
SELECT * FROM people WHERE name ~* '\mJack';
因为
SELECT * FROM people WHERE to_tsvector('english', name) @@ to_tsquery('english', 'Jack:*');
注意:但是,如果您的查询过滤器 (Jack
) 来自用户输入,则上述两个查询都需要一些保护(即在 ILIKE
一个你需要转义 %
和 _
字符,在正则表达式中你需要转义更多,而在 FTS 一个中,你需要用一些解析器解析查询 &生成有效的 FTS' tsquery
查询,因为如果 to_tsquery()
的参数无效,则会给您一个错误。在 plainto_tsquery()
中,您不能使用前缀匹配查询)。
注 2:带有 name ~* '\mJack'
的正则表达式变体最适用于英文名称。如果你想使用整个范围的 unicode(即你想使用字符,比如 æ
),你需要一个稍微不同的模式。类似于:
SELECT * FROM people WHERE name ~* '(^|\s|,)Jack';
这将适用于大多数名称,此外,对于一些旧名称,这也将像真正的前缀匹配一样工作,例如 O'Brian
。