哪个 Postgresql 索引对于基于相似性查询的文本列最有效

Posted

技术标签:

【中文标题】哪个 Postgresql 索引对于基于相似性查询的文本列最有效【英文标题】:Which Postgresql index is most efficient for text column with queries based on similarity 【发布时间】:2022-01-14 05:53:13 【问题描述】:

我想为以下用例在文本列上创建索引。我们有一个Segment 的表,其中有一列content 类型为文本。我们使用 pg_trgm 执行基于相似性的查询。这在翻译编辑器中用于查找相似的字符串。 以下是表格详情:

CREATE TABLE public.segments
(
  id integer NOT NULL DEFAULT nextval('segments_id_seq'::regclass),
  language_id integer NOT NULL,
  content text NOT NULL,
  created_at timestamp without time zone NOT NULL,
  updated_at timestamp without time zone NOT NULL,
  CONSTRAINT segments_pkey PRIMARY KEY (id),
  CONSTRAINT segments_language_id_fkey FOREIGN KEY (language_id)
      REFERENCES public.languages (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE,
  CONSTRAINT segments_content_language_id_key UNIQUE (content, language_id)
)

这里是查询(Ruby + Hanami):

def find_by_segment_match(source_text_for_lookup, source_lang, sim_score)
    aggregate(:translation_records)
      .where(language_id: source_lang)
      .where  similarity(:content, source_text_for_lookup) > sim_score/100.00 
      .select_append  float::similarity(:content, source_text_for_lookup).as(:similarity) 
      .order  similarity(:content, source_text_for_lookup).desc 
end

---编辑---

这是查询:

SELECT "id", "language_id", "content", "created_at", "updated_at", SIMILARITY("content", 'This will not work.') AS "similarity" FROM "segments" WHERE (("language_id" = 2) AND (similarity("content", 'This will not work.') > 0.45)) ORDER BY SIMILARITY("content", 'This will not work.') DESC

SELECT "translation_records"."id", "translation_records"."source_segment_id", "translation_records"."target_segment_id", "translation_records"."domain_id",
"translation_records"."style_id",
"translation_records"."created_by", "translation_records"."updated_by", "translation_records"."project_name", "translation_records"."created_at", "translation_records"."updated_at", "translation_records"."language_combination", "translation_records"."uid",
"translation_records"."import_comment" FROM "translation_records" INNER JOIN "segments" ON ("segments"."id" = "translation_records"."source_segment_id") WHERE ("translation_records"."source_segment_id" IN (27548)) ORDER BY "translation_records"."id"

---结束编辑---

---编辑 1---

如何重新索引?最初,我们将导入大约 200 万条旧记录。我们应该在何时以及多久重建一次索引?

---结束编辑1---

像 CREATE INDEX ON Segment USING gist (content) 这样的东西可以吗?我真的找不到哪个可用索引最适合我们的用例。

最好的,塞巴

【问题讨论】:

这会产生什么查询? 我的错误是首先不包括实际查询。请查看编辑。 【参考方案1】:
CREATE INDEX segment_language_id_idx ON segment USING btree (language_id);
CREATE INDEX segment_content_gin ON segment USING gin (content gin_trgm_ops);

【讨论】:

【参考方案2】:

您显示的第二个查询似乎与此问题无关。

您的第一个查询不能使用三元组索引,因为查询必须以运算符形式而不是函数形式编写。

在运算符形式下,它看起来像这样:

SELECT "id", "language_id", "content", "created_at", "updated_at", SIMILARITY("content", 'This will not work.') AS "similarity" 
FROM segments 
WHERE language_id = 2 AND content % 'This will not work.'
ORDER BY content <-> 'This will not work.';

为了使% 等同于similarity("content", 'This will not work.') &gt; 0.45,您首先需要执行set pg_trgm.similarity_threshold TO 0.45;

现在你如何让 ruby​​/hanami 生成这个表单,我不知道。

gin_trgm_ops 索引或 gist_index_ops 索引都可以支持 % 运算符。 只能由 gist_trgm_ops 支持。但很难预测这种支持的效率会有多高。如果您的“内容”列很长或者您要比较的文本很长,那么它不太可能非常有效,尤其是在 gist 的情况下。

理想情况下,您应该按 language_id 对表进行分区。如果不是,那么可能构建一个包含两列的多列索引会有所帮助。

【讨论】:

我最终使用了您的查询,因为我可以使用 Hanami 模型调用原始 SQL 语句。另外,我从第二个答案创建了两个索引。它显着提高了性能。

以上是关于哪个 Postgresql 索引对于基于相似性查询的文本列最有效的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL 索引

PostgreSQL hstore 列性能提升一例

12.PostgreSQL索引

PostgreSQL 多列索引,包括数组

Postgresql 8.3,不使用索引的简单查询

PostgreSQL 性能优化 短查询 覆盖索引,前缀索引,索引和排序