在字段上创建不区分大小写和重音/变音符号不敏感的搜索

Rub*_*_ic 6 postgresql index full-text-search unaccent case-sensitive

我想知道在使用全文搜索时创建一个结合两个函数的索引是否有意义:lower(name)f_unaccent(name)Wheref_unaccent只是我的包装器,使 unaccent 函数不可变。

我确实有一个正在处理的索引:f_unaccent(name)使用varchar_pattern_ops. 我的问题是:组合lowerunaccent函数的索引是否会被 full_text_search 触发lower(f_unaccent(name))

我不知道该lower函数是否对全文搜索算法有用。

Eva*_*oll 6

首先,我们需要澄清一些事情。

模式操作和三元组

这种类型的查询可以在 varchar/text pattern_ops 上工作,

foo LIKE 'bar%'
Run Code Online (Sandbox Code Playgroud)

具有非锚定搜索模式的查询需要trigram

foo LIKE '%bar%'
Run Code Online (Sandbox Code Playgroud)

只要您在查询中保持与索引中相同的序列,它们都可以处理函数表达式。

全文搜索是完全不同的东西。它通过截取词根、小写、删除停用词和一系列其他技巧将您的文本转换为词素。继续阅读:

Unaccent 本身(不建议这样做)

不幸的是,虽然unaccent只是“稳定”而不是“不可变”。因此,如果您尝试在其上创建索引,unaccent(text)则会出现错误functions in index expression must be marked IMMUTABLE. 我认为这就是您创建包装器的原因f_accent,因为它在 Stack Overflow上的Erwins 答案中找到了。看起来像这样,

CREATE EXTENSION unaccent;
CREATE OR REPLACE FUNCTION f_unaccent(text)
  RETURNS text AS
$func$
SELECT public.unaccent('public.unaccent', $1)  -- schema-qualify function and dictionary
$func$
LANGUAGE sql
IMMUTABLE;

CREATE TABLE foo AS
  SELECT 'asdf'::text AS mytext;
CREATE INDEX ON foo (lower(f_unaccent(mytext)) text_pattern_ops);
Run Code Online (Sandbox Code Playgroud)

或者,对于三元组,

CREATE INDEX ON foo
USING gin(lower(f_unaccent(mytext)) gin_trgm_ops);
Run Code Online (Sandbox Code Playgroud)

现在请记住,您必须执行这么长的序列lower(f_unaccent(mytext)) 才能使其工作。

SELECT *
FROM foo
WHERE lower(f_unaccent(mytext)) LIKE '%whatever%';
Run Code Online (Sandbox Code Playgroud)

带有 FTS 字典的非重音

而不是那种方法,我会创建一个使用unaccent和使用 FTS的字典。使用自定义词典,虽然它可能会做你想要的。FTS 将单词转换为标记,这使得搜索变得非常简单和快速。

CREATE EXTENSION unaccent; -- still required it plugs into FTS.

CREATE TEXT SEARCH CONFIGURATION mydict ( COPY = simple );
ALTER TEXT SEARCH CONFIGURATION mydict
  ALTER MAPPING FOR hword, hword_part, word
  WITH unaccent, simple;
Run Code Online (Sandbox Code Playgroud)

现在你应该能够

你可以在这里看到令牌和它在做什么,

select to_tsvector(
  'mydict',
  'Zoë and Renée are elected to the council of Cheese Eating Surrender Monkeys'
);
                                                             to_tsvector                                                             
-------------------------------------------------------------------------------------------------------------------------------------
 'and':2 'are':4 'cheese':10 'council':8 'eating':11 'elected':5 'monkeys':13 'of':9 'renee':3 'surrender':12 'the':7 'to':6 'zoe':1
(1 row)
Run Code Online (Sandbox Code Playgroud)

你可以像这样简单地查询表,

SELECT *
FROM foo
WHERE to_tsvector('mydict', mytext) @@ 'cheese & council';
Run Code Online (Sandbox Code Playgroud)

你也不必使用simple。如果该列仅包含一种语言的语句,您可以使用其他词典之一来简化内容并删除停用词。