【问题标题】:Similar UTF-8 strings for autocomplete field自动完成字段的类似 UTF-8 字符串
【发布时间】:2012-05-01 12:22:54
【问题描述】:

背景

用户可以输入名称,系统应该匹配文本,即使用户输入或数据库字段包含重音 (UTF-8) 字符。这是使用pg_trgm 模块。

问题

代码如下:

  SELECT
    t.label
  FROM
    the_table t
  WHERE
    label % 'fil'
  ORDER BY
    similarity( t.label, 'fil' ) DESC

当用户键入fil 时,查询匹配filbert 但不匹配filé powder。 (因为重音字符?)

失败的解决方案 #1

我尝试实现unaccent 函数并将查询重写为:

  SELECT
    t.label
  FROM
    the_table t
  WHERE
    unaccent( label ) % unaccent( 'fil' )
  ORDER BY
    similarity( unaccent( t.label ), unaccent( 'fil' ) ) DESC

这只会返回filbert

失败的解决方案 #2

按照建议:

CREATE EXTENSION pg_trgm;
CREATE EXTENSION unaccent;

CREATE OR REPLACE FUNCTION unaccent_text(text)
  RETURNS text AS
$BODY$
  SELECT unaccent($1); 
$BODY$
  LANGUAGE sql IMMUTABLE
  COST 1;

表上的所有其他索引都已删除。那么:

CREATE INDEX label_unaccent_idx 
ON the_table( lower( unaccent_text( label ) ) );

这只会返回一个结果:

  SELECT
    t.label
  FROM
    the_table t
  WHERE
    label % 'fil'
  ORDER BY
    similarity( t.label, 'fil' ) DESC

问题

重写查询以确保返回两个结果的最佳方法是什么?

谢谢!

相关

http://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.0#Unaccent_filtering_dictionary

http://postgresql.1045698.n5.nabble.com/index-refuses-to-build-td5108810.html

【问题讨论】:

  • 您的所有搜索都被锚定了吗?如果是这样,旧的 LIKE 将与使用 text_pattern_ops 的索引(以及您的 unaccent 函数)相结合,为您提供很好的服务。我写了more about that here。另外,你知道PostgreSQL unaccent module吗?
  • 为什么你在没有重音的文字上使用 unaccent,而不是包含重音的列?我希望你在定义索引时也使用了 unaccent 函数。如果是这样,它不会认为在搜索条件上不使用相同的函数就可以使用索引。
  • 搜索没有被锚定。我不知道 unaccent 模块。我会研究不重音索引。我打错了unaccent 电话,失败的解决方案现在反映了现实。
  • 这个问题似乎离题了,因为它更适合dba.stackexchange.com

标签: postgresql utf-8 plpgsql string-comparison similarity


【解决方案1】:

您没有使用pg_trgm 模块提供的操作符类。我会创建一个这样的索引:

创建索引 label_Lower_unaccent_trgm_idx ON test_trgm 使用 gist (lower(unaccent_text(label)) gist_trgm_ops);

最初,我在这里有一个 GIN 索引,但后来我了解到 GiST 可能更适合这种查询,因为它可以返回按相似度排序的值。更多详情:

您的查询必须与索引表达式匹配才能使用它。

SELECT label
FROM   the_table
WHERE  lower(unaccent_text(label)) % 'fil'
ORDER  BY similarity(label, 'fil') DESC -- it's ok to use original string here

但是,根据 % 运算符,“filbert”和“filé powder”实际上与“fil”不太相似。我怀疑你真正想要的是这个:

选择标签 FROM the_table WHERE lower(unaccent_text(label)) ~~ '%fil%' ORDER BY Similarity(label, 'fil') DESC -- 这里可以使用原始字符串

这将找到所有包含搜索字符串的字符串,并首先根据%运算符对最佳匹配进行排序。

还有有趣的部分:表达式可以使用自 PostgreSQL 9.1 以来的 GIN 或 GiST 索引!我引用manual on the pg_trgm moule

从 PostgreSQL 9.1 开始,这些索引类型也支持索引 搜索 LIKE 和 ILIKE,例如


如果您真的打算使用 % 运算符:

您是否尝试过降低阈值为相似性运算符%set_limit()

SELECT set_limit(0.1);

甚至更低?默认值为 0.3。只是看看它是否是过滤额外匹配的阈值。

【讨论】:

  • 谢谢你,欧文;可能是set_limit
  • @DaveJarvis:在recent test results 之后,我修改了我的答案以使用 GiST 索引而不是 GIN。你可能会感兴趣。 (用链接重新发布。)
【解决方案2】:

PostgreSQL 9.1 的解决方案:

-- Install the requisite extensions.
CREATE EXTENSION pg_trgm;
CREATE EXTENSION unaccent;

-- Function fixes STABLE vs. IMMUTABLE problem of the unaccent function.
CREATE OR REPLACE FUNCTION unaccent_text(text)
  RETURNS text AS
$BODY$
  -- unaccent is STABLE, but indexes must use IMMUTABLE functions.
  SELECT unaccent($1); 
$BODY$
  LANGUAGE sql IMMUTABLE
  COST 1;

-- Create an unaccented index.
CREATE INDEX the_table_label_unaccent_idx
ON the_table USING gin (lower(unaccent_text(label)) gin_trgm_ops);

-- Define the matching threshold.
SELECT set_limit(0.175);

-- Test the query (matching against the index expression).
SELECT
  label
FROM
  the_table
WHERE
  lower(unaccent_text(label)) % 'fil'
ORDER BY
  similarity(label, 'fil') DESC 

返回“filbert”、“fish fillet”和“filé powder”。

无需调用SELECT set_limit(0.175);,您可以使用双波浪号(~~)运算符:

-- Test the query (matching against the index expression).
SELECT
  label
FROM
  the_table
WHERE
  lower(unaccent_text(label)) ~~ 'fil'
ORDER BY
  similarity(label, 'fil') DESC 

还返回“filbert”、“fish fillet”和“filé powder”。

【讨论】:

  • +1 我喜欢您以可靠的格式为其他人提供您的解决方案。还解决了 STABLE 与 IMMUTABLE 的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 2017-03-04
  • 2015-01-19
  • 2021-09-08
  • 1970-01-01
  • 2013-03-16
相关资源
最近更新 更多