PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Come evitare il carattere punto interrogativo (?) con Spring JpaRepository

In caso di escape da ? non è possibile, puoi creare un operatore duplicato con nome diverso.

Nuovo operatore

Sintassi per la creazione di operatori in 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 ]
)

In caso di ?| utilizzato in jsonb sarà:

CREATE OPERATOR ^|(
  PROCEDURE = jsonb_exists_any,
  LEFTARG = jsonb,
  RIGHTARG = _text,
  RESTRICT = contsel,
  JOIN = contjoinsel);

Ho usato ^| ad esempio, nome alternativo. Può essere qualsiasi sequenza da questo elenco:+ - * / < > = ~ ! @ # % ^ & | ?`.

Puoi trovare la definizione corrente per l'operatore che ti interessa interrogando la tabella pg_catalog.pg_operator.

SELECT oid, *
  FROM pg_catalog.pg_operator
 WHERE oprname = '?|'
   AND oprleft = (SELECT oid FROM pg_type WHERE typname = 'jsonb');

Puoi anche utilizzare strumenti GUI come pgAdmin e sfogliare pg_catalog per ottenere la definizione SQL pronta per il riutilizzo.

Abilitazione dell'indice

Se si desidera utilizzare index per questo "nuovo" operatore, sarà necessario creare una nuova classe di operatore e facoltativamente una famiglia. Nel nostro caso, abbiamo bisogno di entrambi, dal momento che non possiamo aggiungerlo alla famiglia esistente, perché l'operatore predefinito sta già occupando lo slot della strategia.

Proprio come con gli operatori, si consiglia di utilizzare uno strumento GUI come pgAdmin per sfogliare le classi di operatori e copiarlo e incollarlo.

Innanzitutto, prendiamo l'OID dell'operatore di cui abbiamo duplicato:

SELECT oid, *
  FROM pg_catalog.pg_operator
 WHERE oprname = '?|'
   AND oprleft = (SELECT oid FROM pg_type WHERE typname = 'jsonb');

Stessa cosa per la famiglia dell'operatore (la otterremo invece dalla tabella della classe dell'operatore), stiamo cercando la classe gin poiché questa è quella che supporta ?| . opcdefault viene utilizzato, perché esiste una classe opzionale jsonb_path_ops che non supporta questo operatore:

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

Quindi otteniamo la strategia utilizzata dall'operatore che abbiamo duplicato:

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

Quindi le funzioni utilizzate dalla 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

Questo ci porta a questa classe di operatori. Creerà una famiglia di operatori se non esiste già.

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);

Ora devi solo creare l'indice usando il nome dell'operatore che è stato creato, qualcosa come:

CREATE INDEX ON jsonb_table USING gin(jsonb_column jsonb_ops_custom)

E dovresti essere in grado di usare index:

SET enable_seqscan = off;
EXPLAIN ANALYZE
SELECT * FROM jsonb_table WHERE jsonb_column ^| array['b', 'c'];