Estou tentando criar um índice que dará suporte a consultas que usam meu operador personalizado. Isso está no PostgreSQL 10.4.
O operador personalizado
Eu segui as dicas nesta resposta SO para criar um operador que executa a correspondência de estilo "LIKE" em elementos em um texto ARRAY.
CREATE FUNCTION reverse_like (text, text) returns boolean language sql
as $$ select $2 like $1 $$;
CREATE OPERATOR <~~ ( function =reverse_like, leftarg = text, rightarg=text );
O operador acima me permite fazer coisas como
SELECT 'ab%' <~~ ANY('{"abc","def"}');
O esquema, índice e consulta
Eu tenho uma tabela com visitas de tráfego da web chamada sessions
que inclui uma coluna de matriz.
CREATE TABLE sessions
(
session_id varchar(24) NOT NULL,
first_seen timestamp,
domains varchar[]
);
Para consultar a coluna de domínios para ver se um determinado domínio (ou nome de domínio parcial/curinga) foi visitado, posso fazer o seguinte:
SELECT count(*)
FROM session_4070ba14_f081_41cb_9ef7_9dd385934da7
WHERE 'www.foo%' <~~ ANY(domains);
Eu quero acelerar as consultas acima com o índice GIN. Então eu criei o índice da seguinte forma:
CREATE INDEX idx_domains ON session USING GIN(domains);
A questão
Depois de executar analise na mesa e set enable_seqscan = false;
não tenho sorte em fazer o Postgres empregar esse índice. Está sempre fazendo um seqscan. Ele usa o índice acima de operadores de matriz, @>
mas não para o meu <~~
operador personalizado.
Eu acho que é porque o índice GIN não sabe como lidar com meu operador personalizado - então eu preciso criar uma classe de operador e depois criar meu índice usando isso? Ou crio um índice funcional?
Para suporte a trigramas, você pode tentar a extensão parray_gin
Se você quer apenas fazer a correspondência de prefixo (mais eficiente do que a fornecida pelo trigrama), não acho que haja como fazer isso sem escrever algum código C para colar as peças. Acho que você trabalharia diretamente no tipo de matriz, portanto, não precisaria do ANY e, portanto, não se beneficiaria do operador reverse_like.
Você não poderá indexar uma expressão como esta:
<constant> <operator> ANY(<array column>)
Sua única chance seria definir um operador de tal forma que sua expressão se parecesse com:
<array column> <operator> <constant>
Mas escrever uma classe de operador GIN significa escrever uma extensão em C, e não acho que você queira ir tão longe.
A solução fácil seria alterar seu modelo de dados para que você não use arrays para coisas assim.
Acontece que eu compliquei isso pensando em um índice GIN. Um índice b-tree em todo o array funciona bem e suporta o operador personalizado <~~.