多列上的卦索引是否会使搜索更快以及如何正确进行此类搜索?
Will trigram index on multiple columns make search faster and how to make such search properly?
假设我有 table 多列。例如:
id int
name text
surname text
cars json
示例记录为
+----+------+---------+------------------------------------+
| id | name | surname | cars |
+----+------+---------+------------------------------------+
| 1 | John | Doe | {"values":["Ford", "BMW", "Fiat"]} |
+----+------+---------+------------------------------------+
我想像这样搜索所有这些 table 数据的相关性:
select *,
similarity(
'Malcolm Joe likes Ferrary, but hates BMW',
(name || (cars ->> 'values') || surname)
) sim
from public.test_table
where similarity(
'Malcolm Joe likes Ferrary, but hates BMW',
(name || (cars ->> 'values') || surname)
) > 0.05
order by sim desc;
有什么方法可以加快搜索速度吗?创建三元组索引?如果是这样-如何更好地创建它?在一列上,在每一列上,在串联表达式上?另外,我不明白哪种类型的索引更好——GIN 或 GiST。我读过 GIN 通常更适合常规全文搜索,但 GiST 更适合三元组搜索。对吗?
我也想问下上面的查询有没有更好的写法?
如果有人想知道我为什么选择 trigram,而不是常规的全文搜索 - 这是因为搜索字符串将来自处理一些用户输入,所以当英文 'o' 或 'c' 被西里尔字母取代。我的数据库记录或搜索也可以包含字母数字数据,也可以用三元组更好地处理。
在这种情况下,您需要一个 GiST 索引,因为只有它可以与使用三元组距离运算符的 ORDER BY
查询一起使用:
CREATE INDEX ON public.test_table USING gist
((name || (cars ->> 'values') || surname) gist_trgm_ops);
然后应将查询重写为:
SELECT *,
similarity(
'Malcolm Joe likes Ferrary, but hates BMW',
(name || (cars ->> 'values') || surname)
) sim
FROM public.test_table
WHERE ((name || (cars ->> 'values') || surname)
<->
'Malcolm Joe likes Ferrary, but hates BMW')
< 0.95
ORDER BY (name || (cars ->> 'values') || surname)
<-> /* trigram distance */
'Malcolm Joe likes Ferrary, but hates BMW'
LIMIT 50;
必须重写查询,因为 <->
有索引支持,但 ORDER BY
表达式中的 similarity()
没有索引支持。
我添加了LIMIT
来提示优化器,适当地设置一个限制。
我 认为 一般而言,GIN 索引对大型表表现更好,但我不确定。无论如何,您对此查询别无选择,因为 GIN 索引不支持该 ORDER BY
子句。
根据您的示例,您可能希望在表达式 (name || (cars ->> 'values') || surname)
上创建索引。但是,您的示例本身没有意义。它是有效的 SQL,但您到底为什么要这样做呢?你为什么要将一个英文句子与一个由某人的全名组成但中间注入了 JSON 的字符串进行比较?这很重要,因为您的示例只有一行,所以索引无关紧要。因此,我们必须将您的示例外推到大量行,其中索引很重要。但由于它在现实世界中没有任何意义,我们如何才能以一种合理的方式对其进行推断?
Also, I haven't understand which type of index is better - GIN or GiST. I've read that GIN is usually better for regular full text search, but GiST is better for trigram search. Is that correct?
根据我的经验,通常情况并非如此。 GiST trigram 索引是基于签名的,其中每个 trigram 在签名中设置一个位。但是 trigram 的数量远远多于 bit 的数量,因此它们严重超载。这些类型的索引仅在填充较少时表现良好。 (但是很难提前说 "lightly populated" 是什么意思,其他的 "try it with your real dataset and see"。)鉴于它们的不可预测性,我避免使用 GiST 索引,除非它们有明显的好处,我没有看到这里。
鉴于您的查询,您可以使用任何一种索引,但必须以不同的方式编写。此外,这两个索引是否有帮助是值得怀疑的,因为在
similarity(x,exp) > 0.05
0.05 的截止值非常宽松,索引可能会拒绝很少的行。
如果你有一个更高的截止值,比如 0.5,那么使用 GIN 索引可以将其公式化为:
set pg_trgm.similarity_threshold = 0.5;
select ... from test_table where x % exp order by x <-> exp ;
这将提取足够相似的所有内容,然后按距离对它们进行排序。如果 "sufficiently similar" 足够少,这会提供相当不错的性能(如果不是,您应该重新选择 pg_trgm.similarity_threshold)。正如 Laurenz Albe 所说,使用 GiST 索引,您可以按顺序提取行,然后在达到 LIMIT 时停止,但在没有 LIMIT 子句的情况下,这是没有价值的。
假设我有 table 多列。例如:
id int
name text
surname text
cars json
示例记录为
+----+------+---------+------------------------------------+
| id | name | surname | cars |
+----+------+---------+------------------------------------+
| 1 | John | Doe | {"values":["Ford", "BMW", "Fiat"]} |
+----+------+---------+------------------------------------+
我想像这样搜索所有这些 table 数据的相关性:
select *,
similarity(
'Malcolm Joe likes Ferrary, but hates BMW',
(name || (cars ->> 'values') || surname)
) sim
from public.test_table
where similarity(
'Malcolm Joe likes Ferrary, but hates BMW',
(name || (cars ->> 'values') || surname)
) > 0.05
order by sim desc;
有什么方法可以加快搜索速度吗?创建三元组索引?如果是这样-如何更好地创建它?在一列上,在每一列上,在串联表达式上?另外,我不明白哪种类型的索引更好——GIN 或 GiST。我读过 GIN 通常更适合常规全文搜索,但 GiST 更适合三元组搜索。对吗?
我也想问下上面的查询有没有更好的写法?
如果有人想知道我为什么选择 trigram,而不是常规的全文搜索 - 这是因为搜索字符串将来自处理一些用户输入,所以当英文 'o' 或 'c' 被西里尔字母取代。我的数据库记录或搜索也可以包含字母数字数据,也可以用三元组更好地处理。
在这种情况下,您需要一个 GiST 索引,因为只有它可以与使用三元组距离运算符的 ORDER BY
查询一起使用:
CREATE INDEX ON public.test_table USING gist
((name || (cars ->> 'values') || surname) gist_trgm_ops);
然后应将查询重写为:
SELECT *,
similarity(
'Malcolm Joe likes Ferrary, but hates BMW',
(name || (cars ->> 'values') || surname)
) sim
FROM public.test_table
WHERE ((name || (cars ->> 'values') || surname)
<->
'Malcolm Joe likes Ferrary, but hates BMW')
< 0.95
ORDER BY (name || (cars ->> 'values') || surname)
<-> /* trigram distance */
'Malcolm Joe likes Ferrary, but hates BMW'
LIMIT 50;
必须重写查询,因为 <->
有索引支持,但 ORDER BY
表达式中的 similarity()
没有索引支持。
我添加了LIMIT
来提示优化器,适当地设置一个限制。
我 认为 一般而言,GIN 索引对大型表表现更好,但我不确定。无论如何,您对此查询别无选择,因为 GIN 索引不支持该 ORDER BY
子句。
根据您的示例,您可能希望在表达式 (name || (cars ->> 'values') || surname)
上创建索引。但是,您的示例本身没有意义。它是有效的 SQL,但您到底为什么要这样做呢?你为什么要将一个英文句子与一个由某人的全名组成但中间注入了 JSON 的字符串进行比较?这很重要,因为您的示例只有一行,所以索引无关紧要。因此,我们必须将您的示例外推到大量行,其中索引很重要。但由于它在现实世界中没有任何意义,我们如何才能以一种合理的方式对其进行推断?
Also, I haven't understand which type of index is better - GIN or GiST. I've read that GIN is usually better for regular full text search, but GiST is better for trigram search. Is that correct?
根据我的经验,通常情况并非如此。 GiST trigram 索引是基于签名的,其中每个 trigram 在签名中设置一个位。但是 trigram 的数量远远多于 bit 的数量,因此它们严重超载。这些类型的索引仅在填充较少时表现良好。 (但是很难提前说 "lightly populated" 是什么意思,其他的 "try it with your real dataset and see"。)鉴于它们的不可预测性,我避免使用 GiST 索引,除非它们有明显的好处,我没有看到这里。
鉴于您的查询,您可以使用任何一种索引,但必须以不同的方式编写。此外,这两个索引是否有帮助是值得怀疑的,因为在
similarity(x,exp) > 0.05
0.05 的截止值非常宽松,索引可能会拒绝很少的行。
如果你有一个更高的截止值,比如 0.5,那么使用 GIN 索引可以将其公式化为:
set pg_trgm.similarity_threshold = 0.5;
select ... from test_table where x % exp order by x <-> exp ;
这将提取足够相似的所有内容,然后按距离对它们进行排序。如果 "sufficiently similar" 足够少,这会提供相当不错的性能(如果不是,您应该重新选择 pg_trgm.similarity_threshold)。正如 Laurenz Albe 所说,使用 GiST 索引,您可以按顺序提取行,然后在达到 LIMIT 时停止,但在没有 LIMIT 子句的情况下,这是没有价值的。