I have a lot of queries using
...
WHERE company_id = 2
AND unaccent(name) ILIKE unaccent('value');
Is possible to create a index to optimize this type of query? I take a look at GIN and pg_trgm but what about the unaccent()
part?
The tricky part is that unaccent()
is only STABLE
, but indexes demand IMMUTABLE
expressions. You can work around this by creating an immutable variant.
This code-path requires the privilege to create C functions. And the second part uses SQL-standard syntax that requires Postgres 14 or later:
CREATE OR REPLACE FUNCTION public.immutable_unaccent(regdictionary, text)
RETURNS text
LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS
'$libdir/unaccent', 'unaccent_dict';
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text
LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT
RETURN public.immutable_unaccent(regdictionary 'public.unaccent', $1);
Then create an expression index with the IMMUTABLE
function, and use it in queries:
CREATE INDEX tbl_unaccent_name_idx ON tbl (public.f_unaccent(name));
SELECT * FROM tbl
WHERE f_unaccent(name) = f_unaccent('value');
Or a GIN index using the additional module pg_trgm:
CREATE INDEX tbl_unaccent_name_trgm_idx ON tbl USING gin (public.f_unaccent(name) gin_trgm_ops);
SELECT * FROM tbl
WHERE f_unaccent(name) ~* f_unaccent('value');
There is a lot of fine print to this. Detailed instructions (incl. variants for limited privileges and older versions):
About pattern matching operators LIKE
, ILIKE
, ~
, ~*
& friends:
And you'll want to keep an eye on special characters in the search pattern. See: