PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Index pour trouver un élément dans un tableau JSON

jsonb dans Postgres 9.4+

Le type de données JSON binaire jsonb améliore largement les options d'index. Vous pouvez maintenant avoir un index GIN sur un jsonb tableau directement :

CREATE TABLE tracks (id serial, artists jsonb);  -- !
CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists);

Pas besoin d'une fonction pour convertir le tableau. Cela prendrait en charge une requête :

SELECT * FROM tracks WHERE artists @> '[{"name": "The Dirty Heads"}]';

@> étant le jsonb l'opérateur "contient", qui peut utiliser l'index GIN. (Pas pour json , uniquement jsonb !)

Ou vous utilisez la classe d'opérateur GIN plus spécialisée et non par défaut jsonb_path_ops pour l'index :

CREATE INDEX tracks_artists_gin_idx ON tracks
USING  gin (artists jsonb_path_ops);  -- !

Même requête.

Actuellement jsonb_path_ops ne prend en charge que le @> opérateur. Mais c'est généralement beaucoup plus petit et plus rapide. Il y a plus d'options d'index, détails dans le manuel .

Si la colonne artists ne contient que les noms affichés dans l'exemple, il serait plus efficace de stocker uniquement les valeurs sous forme de texte JSON primitives et la clé redondante peut être le nom de la colonne.

Notez la différence entre les objets JSON et les types primitifs :

  • Utilisation des index dans le tableau json dans PostgreSQL
CREATE TABLE tracks (id serial, artistnames jsonb);
INSERT INTO tracks  VALUES (2, '["The Dirty Heads", "Louis Richards"]');

CREATE INDEX tracks_artistnames_gin_idx ON tracks USING gin (artistnames);

Requête :

SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads';

? ne fonctionne pas pour les valeurs d'objet , juste des touches et éléments de tableau .

Ou :

CREATE INDEX tracks_artistnames_gin_idx ON tracks
USING  gin (artistnames jsonb_path_ops);

Requête :

SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb;

Plus efficace si les noms sont très dupliqués.

json dans Postgres 9.3+

Cela devrait fonctionner avec un IMMUTABLE fonction :

CREATE OR REPLACE FUNCTION json2arr(_j json, _key text)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT elem->>_key FROM json_array_elements(_j) elem)';

Créez cet index fonctionnel :

CREATE INDEX tracks_artists_gin_idx ON tracks
USING  gin (json2arr(artists, 'name'));

Et utilisez une requête comme ça. L'expression dans WHERE la clause doit correspondre à celle de l'index :

SELECT * FROM tracks
WHERE  '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));

Mis à jour avec des commentaires dans les commentaires. Nous devons utiliser des opérateurs de tableau pour prendre en charge l'index GIN.
L'opérateur "est contenu par" <@ dans ce cas.

Remarques sur la volatilité des fonctions

Vous pouvez déclarer votre fonction IMMUTABLE même si json_array_elements() n'est pas ne l'était pas.
La plupart des JSON les fonctions étaient uniquement STABLE , pas IMMUTABLE . Il y a eu une discussion sur la liste des hackers pour changer cela. La plupart sont IMMUTABLE à présent. Vérifiez avec :

SELECT p.proname, p.provolatile
FROM   pg_proc p
JOIN   pg_namespace n ON n.oid = p.pronamespace
WHERE  n.nspname = 'pg_catalog'
AND    p.proname ~~* '%json%';

Les index fonctionnels ne fonctionnent qu'avec IMMUTABLE fonctions.