2

背景

用户可以输入名称并且系统应该匹配文本,即使用户输入或数据库字段包含重音 (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

4

2 回答 2

5

您没有使用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 索引!我引用了pg_trgm moule 上的手册

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


如果您实际上打算使用%运算符:

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

SELECT set_limit(0.1);

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

于 2012-04-19T23:07:35.560 回答
4

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”。

于 2012-04-19T23:16:34.110 回答