sql >> Base de Datos >  >> RDS >> PostgreSQL

Índice para encontrar un elemento en una matriz JSON

jsonb en PostgreSQL 9.4+

El tipo de datos JSON binario jsonb mejora en gran medida las opciones de índice. Ahora puede tener un índice GIN en un jsonb matriz directamente:

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

No es necesaria una función para convertir la matriz. Esto apoyaría una consulta:

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

@> siendo el jsonb operador "contiene", que puede utilizar el índice GIN. (No para json , solo jsonb !)

O utiliza la clase de operador GIN no predeterminada más especializada jsonb_path_ops para el índice:

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

Misma consulta.

Actualmente jsonb_path_ops solo admite el @> operador. Pero por lo general es mucho más pequeño y más rápido. Hay más opciones de índice, detalles en el manual .

Si la columna artists solo contiene nombres como se muestra en el ejemplo, sería más eficiente almacenar solo los valores como texto JSON primitivas y la clave redundante puede ser el nombre de la columna.

Tenga en cuenta la diferencia entre los objetos JSON y los tipos primitivos:

  • Uso de índices en una matriz json en 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);

Consulta:

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

? no funciona para objetos valores , solo teclas y elementos de matriz .

O:

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

Consulta:

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

Más eficiente si los nombres son muy duplicados.

json en PostgreSQL 9.3+

Esto debería funcionar con un IMMUTABLE función :

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

Cree este índice funcional :

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

Y usa una consulta Me gusta esto. La expresión en WHERE la cláusula tiene que coincidir con la del índice:

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

Actualizado con comentarios en los comentarios. Necesitamos usar operadores de matriz para admitir el índice GIN.
El operador "está contenido en" <@ en este caso.

Notas sobre la volatilidad de funciones

Puedes declarar tu función IMMUTABLE incluso si json_array_elements() no es no lo era.
La mayoría de JSON las funciones solían ser solo STABLE , no IMMUTABLE . Hubo una discusión sobre la lista de hackers para cambiar eso. La mayoría son IMMUTABLE ahora. Consulte con:

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%';

Los índices funcionales solo funcionan con IMMUTABLE funciones.