【问题标题】:Quick Search with autocorrect (GIN INDEX and PG_TRGM extension)自动更正快速搜索(GIN INDEX 和 PG_TRGM 扩展)
【发布时间】:2021-10-29 23:06:42
【问题描述】:

我正在测试一种简单的搜索机制来处理小错别字/拼写错误。类似于自动更正机制。

我为此苦苦挣扎。所以我正在创建一个函数(pl/pgsql)来处理这个问题,并且我在 SUPABASE.IO、PostgreSQL 13.3(类似于 RDS)上运行它。

我想:

  • 将返回的结果限制为仅高度相似的电子邮件地址,例如相似度 > 0.7;
  • 使用 INDEX,因为实际的电子邮件列表将在数千万的数量级,因此必须在一秒钟内返回。
DROP TABLE IF EXISTS email;
CREATE TABLE email (
  email_address TEXT NOT NULL UNIQUE,
  person_id UUID NOT NULL, 
  CONSTRAINT email_pk PRIMARY KEY (email_address)
);

DROP INDEX IF EXISTS email_address_trigram_idx;
CREATE INDEX email_address_trigram_idx ON email USING gin(email_address gin_trgm_ops);

INSERT INTO email(email_address, person_id) VALUES
  ('test100@gmail.com', uuid_generate_v4())
, ('100test@gmail.com', uuid_generate_v4())
, ('testoo1000@gmail.com', uuid_generate_v4())
, ('test1001@gmail.com', uuid_generate_v4())
, ('test100@gmial.com', uuid_generate_v4())
, ('test200@gmail.com', uuid_generate_v4())
, ('200test@gmail.com', uuid_generate_v4())
, ('testoo2000@gmail.com', uuid_generate_v4())
, ('test2002@gmail.com', uuid_generate_v4())
, ('test200@gmial.com', uuid_generate_v4())
, ('test300@gmail.com', uuid_generate_v4())
, ('300test@gmail.com', uuid_generate_v4())
, ('testoo3000@gmail.com', uuid_generate_v4())
, ('test3003@gmail.com', uuid_generate_v4())
, ('test300@gmial.com', uuid_generate_v4())
, ('test400@gmail.com', uuid_generate_v4())
, ('400test@gmail.com', uuid_generate_v4())
, ('testoo4000@gmail.com', uuid_generate_v4())
, ('test4004@gmail.com', uuid_generate_v4())
, ('test400@gmial.com', uuid_generate_v4())
, ('tset100@gmail.com', uuid_generate_v4())
, ('100tset@gmail.com', uuid_generate_v4())
, ('tsetoo1000@gmail.com', uuid_generate_v4())
, ('tset1001@gmail.com', uuid_generate_v4())
, ('tset100@gmial.com', uuid_generate_v4())
, ('tset200@gmail.com', uuid_generate_v4())
, ('200tset@gmail.com', uuid_generate_v4())
, ('tsetoo2000@gmail.com', uuid_generate_v4())
, ('tset2002@gmail.com', uuid_generate_v4())
, ('tset200@gmial.com', uuid_generate_v4())
, ('tset300@gmail.com', uuid_generate_v4())
, ('300tset@gmail.com', uuid_generate_v4())
, ('tsetoo3000@gmail.com', uuid_generate_v4())
, ('tset3003@gmail.com', uuid_generate_v4())
, ('tset300@gmial.com', uuid_generate_v4())
, ('tset400@gmail.com', uuid_generate_v4())
, ('400tset@gmail.com', uuid_generate_v4())
, ('tsetoo4000@gmail.com', uuid_generate_v4())
, ('tset4004@gmail.com', uuid_generate_v4())
, ('tset400@gmial.com', uuid_generate_v4())
, ('different_email@yahoo.com', uuid_generate_v4());

SET pg_trgm.similarity_threshold = 0.8; -- This doesn't seem to affect my queries

SELECT *, similarity('tesd100@gmail.com', email_address)
FROM email
WHERE email_address % 'tesd100@gmail.com';

我想要一种快速搜索并且仍能容忍搜索中出现一些小错误的方法。

【问题讨论】:

  • 我将 SET_LIMIT(0.85) 添加到查询中,它现在似乎可以工作了。不过,我需要对其进行更多测试。
  • 我现在不知道如何在 PL/PGSQL 函数中使用 % 运算符...我试过
  • 您可以使用嵌套在 PL/pgSQL 函数中的相同 SQL 语句。而且您不需要第 13 页中已弃用的SET_LIMIT()。请使用pg_trgm.similarity_threshold,如问题所示。

标签: postgresql plpgsql fuzzy-search autocorrect supabase


【解决方案1】:

首先,您的表定义在(email_address) 上创建两个 唯一索引。别。去掉UNIQUE约束,保留PK:

CREATE TABLE email (
  email_address text PRIMARY KEY
, person_id uuid NOT NULL  -- bigint?
);

(也不确定为什么需要uuid 来代替person_id。世界上没有足够的人来证明bigint 的合理性。)

接下来,既然你想...

将返回的结果限制为仅高度相似的电子邮件地址,

我建议最近邻搜索。为此目的创建一个 GiST 索引而不是 GIN:

CREATE INDEX email_address_trigram_gist_idx ON email USING gist (email_address gist_trgm_ops);

并使用这样的查询:

SELECT *, similarity('tesd100@gmail.com', email_address)
FROM   email
WHERE  email_address % 'tesd100@gmail.com'
ORDER  BY email_address <-> 'tesd100@gmail.com'  -- note the use of the operator <->
LIMIT  10;

引用the manual:

这可以通过 GiST 索引非常有效地实现,但不能通过 GIN 索引。当只有一个 需要少量最接近的匹配项。

在使用较小的LIMIT 时,可能不需要将pg_trgm.similarity_threshold 设置得非常高,因为此查询首先会为您提供最佳匹配。

相关:

【讨论】:

    猜你喜欢
    • 2017-10-07
    • 1970-01-01
    • 2012-11-12
    • 1970-01-01
    • 1970-01-01
    • 2015-11-10
    • 1970-01-01
    • 2021-02-01
    • 2011-12-24
    相关资源
    最近更新 更多