PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Como escapar do caractere ponto de interrogação (?) com Spring JpaRepository


No caso de escapar ? não for possível, você pode criar um operador duplicado com um nome diferente.

Novo operador


Sintaxe para criar operadores no Postgres:
CREATE OPERATOR name (
    PROCEDURE = function_name
    [, LEFTARG = left_type ] [, RIGHTARG = right_type ]
    [, COMMUTATOR = com_op ] [, NEGATOR = neg_op ]
    [, RESTRICT = res_proc ] [, JOIN = join_proc ]
    [, HASHES ] [, MERGES ]
)

No caso de ?| usado em jsonb será:
CREATE OPERATOR ^|(
  PROCEDURE = jsonb_exists_any,
  LEFTARG = jsonb,
  RIGHTARG = _text,
  RESTRICT = contsel,
  JOIN = contjoinsel);

Eu usei ^| por exemplo, nome alternativo. Pode ser qualquer sequência desta lista:+ - * / < > = ~ ! @ # % ^ & | ?`.

Você pode encontrar a definição atual para o operador de seu interesse consultando a tabela pg_catalog.pg_operator.
SELECT oid, *
  FROM pg_catalog.pg_operator
 WHERE oprname = '?|'
   AND oprleft = (SELECT oid FROM pg_type WHERE typname = 'jsonb');

Você também pode usar a ferramenta GUI como pgAdmin e navegar pelo pg_catalog para preparar a definição SQL para reutilização.

Ativando índice


Se você quiser usar o índice para este "novo" operador, precisará criar uma nova classe de operador e, opcionalmente, uma família. No nosso caso, precisamos de ambos, pois não podemos adicioná-lo à família existente, pois o operador padrão já está ocupando o slot da estratégia.

Assim como com os operadores, é recomendado usar a ferramenta GUI como pgAdmin para navegar pelas classes de operadores e apenas copiar e colar.

Primeiro, pegamos o OID do operador que duplicamos:
SELECT oid, *
  FROM pg_catalog.pg_operator
 WHERE oprname = '?|'
   AND oprleft = (SELECT oid FROM pg_type WHERE typname = 'jsonb');

A mesma coisa para a família de operadores (obteremos da tabela de classes de operadores), estamos procurando a classe gin, pois esta é a que suporta ?| . opcdefault é usado, porque há uma classe opcional jsonb_path_ops que não suporta este operador:
SELECT opcfamily
  FROM pg_opclass
 WHERE opcintype = (SELECT oid FROM pg_type WHERE typname = 'jsonb')
   AND opcmethod = (SELECT oid FROM pg_am WHERE amname = 'gin')
   AND opcdefault

Em seguida, obtemos a estratégia usada pelo operador que duplicamos:
SELECT amopstrategy,
       (SELECT typname FROM pg_type WHERE oid = amoplefttype) AS left_t, 
       (SELECT typname FROM pg_type WHERE oid = amoprighttype) AS right_t,*
FROM pg_amop
WHERE amopfamily = 4036 --family oid
  AND amopopr = 3248 --operator oid

Então funções usadas pela classe:
SELECT amprocnum, amproc::text, pg_get_function_identity_arguments(amproc::oid) AS args,
      (SELECT typname FROM pg_type WHERE oid = amproclefttype) AS left_t,
      (SELECT typname FROM pg_type WHERE oid = amprocrighttype) AS right_t,*
FROM pg_amproc
WHERE amprocfamily = 4036 --op family

Isso nos leva a essa classe de operadores. Ele criará uma família de operadores se ainda não existir.
CREATE OPERATOR CLASS jsonb_ops_custom
   FOR TYPE jsonb USING gin AS
   OPERATOR 10  ^|(jsonb, _text),
   FUNCTION 1  gin_compare_jsonb(text, text),
   FUNCTION 2  gin_extract_jsonb(jsonb, internal, internal),
   FUNCTION 3  gin_extract_jsonb_query(jsonb, internal, smallint, internal, internal, internal, internal),
   FUNCTION 4  gin_consistent_jsonb(internal, smallint, jsonb, integer, internal, internal, internal, internal),
   FUNCTION 6  gin_triconsistent_jsonb(internal, smallint, jsonb, integer, internal, internal, internal);

Agora você só precisa criar o índice usando o nome do operador que foi criado, algo como:
CREATE INDEX ON jsonb_table USING gin(jsonb_column jsonb_ops_custom)

E você deve ser capaz de usar index:
SET enable_seqscan = off;
EXPLAIN ANALYZE
SELECT * FROM jsonb_table WHERE jsonb_column ^| array['b', 'c'];