Estou enfrentando uma situação em que os usuários DEVEM enviar itens escritos em inglês simples. No entanto, acontece que os nomes têm acentos - vindos de vários idiomas. Eu tenho uma tsvector
coluna na minha tabela que é indexada para pesquisa de texto completo.
Aqui estão as informações sobre esta coluna e informações relevantes sobre a tabela:
Table "public.companies"
Column | Type | Modifiers | Storage | Stats target | Description
----------------+------------------------+--------------------------------------------------------+----------+--------------+-------------
id | integer | not null default nextval('companies_id_seq'::regclass) | plain | |
name | character varying(128) | not null | extended | |
description | text | | extended | |
tsmeta | tsvector | | extended | |
Indexes:
"companies_pkey" PRIMARY KEY, btree (id) CLUSTER
"companies_search_idx" gin (tsmeta)
Triggers:
companies_tsmeta_update BEFORE INSERT OR UPDATE ON companies FOR EACH ROW EXECUTE PROCEDURE companies_tsmeta_trigger()
Agora o problema vem quando o usuário cria um nome como français . Nem todos os usuários têm o caractere ç em mãos e aqueles que não têm terão problemas para encontrar essa entrada - o que significa que ela será armazenada com o cedilla-c na tsvector
coluna. Então meu pensamento foi usar a unaccent
extensão para tirar esses acentos. Porém agora os usuários que apenas digitarem français terão problemas e não encontrarão a entrada. Então eu criei uma função que apenas concatena as duas versões (acentuadas e sem acentos) do nome e das descrições. No entanto, as descrições podem ficar muito grandes e eu me preocupo que o índice leve muito armazenamento.
create or replace function companies_tsmeta_trigger() returns trigger as $$
begin
new.tsmeta :=
setweight(to_tsvector('english', coalesce(new.name, '')), 'A') ||
setweight(to_tsvector('english', unaccent(coalesce(new.name, ''))), 'B') ||
setweight(to_tsvector('english', coalesce(new.description, '')), 'C') ||
setweight(to_tsvector('english', unaccent(coalesce(new.description, ''))), 'D');
return new;
end;
$$ language plpgsql;
Quais são suas sugestões?
Meu primeiro impulso foi este: Use um índice de expressão apenas nas strings não acentuadas . Não armazene
tsmeta
na tabela de forma redundante. Então você também não precisa do gatilho. E certamente não inchar o índice com string original e sem acento. Dessa forma, sua tabela e índice são menores e mais rápidos em leitura e gravação.No entanto , a expressão de índice precisa ser
IMMUTABLE
.setweight()
eto_tsvector(regconfig,text)
sãoIMMUTABLE
, isso é bom. Masunaccent()
é sóSTABLE
. Leia a avaliação detalhada aqui primeiro:Se você tiver vários casos de uso, crie o wrapper de função
f_unaccent()
conforme as instruções.Para o caso em questão, podemos envolver toda a
tsvector
criação em uma únicaIMMUTABLE
função:Supondo que o módulo adicional
unaccent
esteja instalado no esquemapublic
, conforme explicado na resposta vinculada.Como
name
é definidoNOT NULL
, não precisamosCOALESCE
dessa coluna.Então o índice pode ficar assim:
Usado em consultas como:
Pensando bem, os pesos não são incluídos em um índice GIN. Portanto, pode valer a pena incluir o valor pré-calculado na tabela. O manual:
Com o índice de expressão demonstrado, a expressão deve ser reavaliada ao usar pesos, o que adiciona um custo extra.
Coluna redundante ou índice de expressão, deve ser uma melhoria integrar o dicionário unaccent
TEXT SEARCH CONFIGURATION
diretamente em um índice personalizado (e coluna da tabela) usando isso, conforme demonstrado por Evan nesta resposta relacionada:Simplifica as coisas e deve ser mais eficiente.