web-dev-qa-db-fra.com

Rechercher des lignes contenant une clé dans un tableau d'enregistrements JSONB

J'essaie de rechercher une clé présente dans un tableau d'objets. Cette structure:

column jdata
{"name": "Somedata",
 "array": [ {"name":"bla1", "attr": "somevalue"}, 
            {"name":"bla2", "otherdata": "somevalue2"},
            {"name":"bla3", "otherdata": "somevalue"}
           ],
"otherstuff": "stuff"
}

Maintenant, je fais btree sur jdata->'name' ou (jdata->'datetime')::cast et cela fonctionne très bien.

Je fais aussi json_path_ops où jdata->'array' @> '[{"name":"bla3"}]' fonctionne un vrai charme.

Mon problème est que la clé attr peut se trouver dans l'un des objets du tableau et je me soucie de l'enregistrement si la clé est présente, mais la valeur peut être presque n'importe quoi. Existe-t-il un moyen de demander cela? Existe-t-il un moyen de l'indexer? Je veux faire jdata->'array' @> '[{"attr": ?}]' Ou peut-être le ? 'attr' peut être utilisé à l'intérieur et dans un tableau en quelque sorte?

Actuellement, je pense à un déclencheur qui recherche la clé, puis la déplace vers un en-tête avec un vrai ou un faux ou autre, puis un btree normal fonctionnera. Y a-t-il une meilleure façon? J'ai besoin de modifier environ 500k enregistrements sur le site moyen pour ajouter ces valeurs.

Veuillez m'orienter dans une direction.

5
Kobus

Ce cas d'utilisation particulier n'est actuellement pas (p. 10) couvert par des index simples pour les opérateurs intégrés.

Requête simple sans prise en charge d'index

SELECT *
FROM   tbl
WHERE  EXISTS (
   SELECT 1
   FROM   jsonb_array_elements(jdata->'array') elem
   WHERE  elem ? 'attr' 
   );

EXISTS parce que nous voulons chaque ligne de qualification une fois , même si plusieurs éléments du tableau peuvent contenir la clé. Et c'est plus rapide.
Mais cette requête ne peut pas utiliser d'index.

Indice d'expression

Vous pouvez générer un tableau de texte de clés uniques dans le tableau d'enregistrements jsonb donné - et encapsuler l'expression dans une simple fonction IMMUTABLE:

CREATE OR REPLACE FUNCTION jsonb_arr_record_keys(jsonb)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY (
   SELECT DISTINCT k
   FROM   jsonb_array_elements($1) elem, jsonb_object_keys(elem) k
   )';

COMMENT ON FUNCTION jsonb_arr_record_keys(jsonb) IS '
   Generates text array of unique keys in jsonb array of records.
   Fails if any array element is not a record!';

Créez ensuite un index d'expression GIN basé sur cette fonction:

CREATE INDEX tbl_special_idx ON tbl USING gin (jsonb_arr_record_keys(jdata->'array'));

Une requête comme celle-ci, utilisant le tableau générique contient l'opérateur @>:

SELECT *
FROM   tbl
WHERE  jsonb_arr_record_keys(jdata->'array') @> '{attr}';

L'index peut désormais être utilisé efficacement.

Indiquez le nom de clé imbriqué dans un tableau ('{attr}'). (Vous pouvez facilement rechercher plusieurs clés de cette façon ('{attr1, attr2}') ou similaire ... )

dbfiddle ici

En relation:

5
Erwin Brandstetter

Existe-t-il un moyen de l'indexer?

Oui, cependant PostgreSQL ne peut pas les tableaux JSON comme ça, vous devrez donc écrire une fonction pour simplifier la structure. Ensuite, vous pouvez indexer sur cette fonction.

1
Evan Carroll