所以我有一张桌子
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万行,因此性能问题,因此我主要关注的是索引。
答案 0 :(得分:2)
Postgres有两种索引类型可以帮助进行全文搜索:GIN和GIST索引(我认为GIN是更常用的索引)。
documentation中的索引简要概述。每个索引类都有更广泛的文档,以及关于该主题的大量博客(here是一个而here是另一个)。
这些可以加快您尝试进行的搜索。
答案 1 :(得分:1)
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';
作为@GordonLinoff answered,full text search也能够通过前缀匹配进行搜索。但是FTS并不是为了有效地做到这一点,它最适合匹配词位。虽然如果你想获得最佳表现,我建议你试一试。&衡量每个。在FTS中,您的查询看起来像这样:
SELECT * FROM people WHERE to_tsvector('english', name) @@ to_tsquery('english', 'Jack:*');
注意:但是,如果您的查询过滤器(Jack
)来自用户输入,则上述两个查询都需要一些保护(即您需要的ILIKE
个查询转义%
和_
字符,在regexp中你需要逃避更多,而在FTS中,你需要用一些解析器解析查询并生成一个有效的FTS' tsquery
查询,因为如果to_tsquery()
的参数无效,plainto_tsquery()
会给您一个错误。而在name ~* '\mJack'
中,您不能使用匹配查询的前缀。)
注释2 :æ
的正则表达式变体最适合英文名称。如果你想使用整个unicode范围(即你想使用像SELECT * FROM people WHERE name ~* '(^|\s|,)Jack';
这样的字符),你需要一个稍微不同的模式。类似的东西:
O'Brian
这适用于大多数名称,此外,它也可以像真正的前缀匹配一些旧名称一样工作,例如class Test {
...
public static getGetters(): string[] {
return Object.keys(this.prototype).filter(name => {
return typeof Object.getOwnPropertyDescriptor(this.prototype, name)["get"] === "function"
});
}
public static getSetters(): string[] {
return Object.keys(this.prototype).filter(name => {
return typeof Object.getOwnPropertyDescriptor(this.prototype, name)["set"] === "function"
});
}
}
Test.getGetters(); // ["RowsCount", "RowsCount2"]
Test.getSetters(); // ["RowsCount", "RowsCount2"]
。
答案 2 :(得分:0)
您可以使用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+'
;
返回:
1 John McEnroe Blackbird Petrus
或者可以使用类似的where substring(name, <regex exp>) is not null
答案 3 :(得分:-1)
如果您知道单词是空格分隔的,则可以
SELECT * FROM people WHERE name LIKE LOWER('Jack%') or name LIKE LOWER(' Jack%') ;
要获得更多控制权,您可以将RegEx与MySQl一起使用