Les types de données JSON sont faits pour stocker des données JSON (JavaScript
Object Notation), comme spécifié dans la RFC 7159. De telles données
peuvent également être stockées comme text
, mais les types de
données JSON ont l'avantage d'assurer que chaque valeur stockée est valide
d'après les règles JSON. Il y a également des fonctions et opérateurs
spécifiques à JSON associés disponibles pour les données stockées dans ces
types de données. Voir Section 9.16.
PostgreSQL propose deux types de données pour stocker des
données JSON : json
et jsonb
.
Pour implémenter des mécanismes de requêtage efficaces pour ces types de
données, PostgreSQL propose aussi le type
jsonpath
décrit dans Section 8.14.7.
Les types de données json
et jsonb
acceptent quasiment des ensembles de valeurs
identiques en entrée. La différence majeure réside dans
l'efficacité. Le type de données json
stocke une copie exacte
du texte en entrée, que chaque fonction doit analyser à chaque exécution,
alors que le type de données jsonb
est stocké dans un format
binaire décomposé qui rend l'insertion légèrement plus lente du fait du
surcoût de la conversion, mais est significativement plus rapide pour traiter
les données, puisqu'aucune analyse n'est nécessaire. jsonb
gère
également l'indexation, ce qui peut être un avantage significatif.
Puisque le type json
stocke une copie exacte du texte en entrée,
il conservera les espaces sémantiquement non significatifs entre les jetons,
ainsi que l'ordre des clés au sein de l'objet JSON. De plus, si un objet
JSON contient dans sa valeur la même clé plus d'une fois, toutes les paires
clé/valeur sont conservées (les fonctions de traitement considèrent la
dernière valeur comme celle significative). À l'inverse, jsonb
ne conserve ni les espaces non significatifs, ni l'ordre des clés d'objet, ni
ne conserve les clés d'objet dupliquées. Si des clés dupliquées sont
présentées en entrée, seule la dernière valeur est conservée.
En général, la plupart des applications devraient préférer stocker les données
JSON avec jsonb
, à moins qu'il y ait des besoins spécifiques,
comme la supposition légitime de l'ordre des clés d'objet.
La RFC 7159 spécifie que les chaînes JSON devraient être encodées en UTF8. Il n'est donc pas possible pour les types JSON de se conformer de manière rigoureuse à la spécification JSON, à moins que l'encodage de la base de données soit UTF8. Tenter d'inclure directement des caractères qui ne peuvent pas être représentés dans l'encodage de la base de données échouera ; inversement, des caractères qui peuvent être représentés dans l'encodage de la base de données, mais pas en UTF8, seront autorisés.
La RFC 7159 autorise les chaînes JSON à contenir des
séquences Unicode échappées, indiquées avec
\u
. Dans la fonction
d'entrée pour le type XXXX
json
, les échappements Unicode sont
autorisés quel que soit l'encodage de la base de données, et sont vérifiés
uniquement pour l'exactitude de la syntaxe (qui est quatre chiffres
hexadécimaux précédés d'un \u
). Toutefois, la fonction
d'entrée pour jsonb
est plus stricte : elle interdit les
échappements Unicode qui ne peuvent pas être représentés dans l'encodage de
la base. Le type jsonb
rejette aussi \u0000
(parce qu'il ne peut pas être représenté avec le type text
de
PostgreSQL), et il insiste pour que chaque
utilisation de paires de substitution Unicode désignant des caractères en
dehors du Unicode Basic Multilingual Plane
soit correcte. Les échappements Unicode valides sont convertis en leur
caractère ASCII ou UTF8 équivalent pour du stockage ; ceci inclut les
« folding surrogate pairs » sur un seul caractère.
De nombreuses fonctions de traitement JSON décrites dans
Section 9.16 convertiront les échappements Unicode
vers des caractères standards, et généreront donc le même type d'erreurs
décrit juste avant si leur entrée est de type json
et non
jsonb
. Le fait que la fonction d'entrée json
ne
fasse pas ces vérifications peut être considéré comme un artefact
historique, bien qu'elle n'autorise pas un simple stockage (sans
traitement) d'échappements Unicode JSON dans un encodage de base qui ne supporte pas les caractères représentés.
Lors de la conversion de données texte JSON vers jsonb
, les
types primitifs décrits par la RFC 7159 sont transcrits
efficacement vers des types PostgreSQL natifs, comme
indiqué dans Tableau 8.23. Par conséquent, il y
a quelques contraintes additionnelles mineures sur ce qui constitue des
données jsonb
valides qui ne s'appliquent ni au type
json
, ni à JSON en définitive, correspondant aux limites de ce
qui peut être représenté par le type de données sous-jacent. Spécifiquement,
jsonb
rejettera les nombres qui sont en dehors de la portée du
type de données numeric
de PostgreSQL,
alors que json
les acceptera. De telles restrictions définies
par l'implémentation sont permises par la RFC 7159.
Cependant, en pratique, de tels problèmes ont beaucoup plus de chances de se
produire dans d'autres implémentations, puisqu'il est habituel de représenter
les types primitifs number
JSON comme des nombres flottants à
double précision (IEEE 754 double precision floating point), ce que la
RFC 7159 anticipe explicitement et autorise. Lorsque JSON
est utilisé comme format d'échange avec de tels systèmes, le risque de perte
de précision pour les valeurs numériques comparées aux données stockées à
l'origine par PostgreSQL devrait être considéré.
À l'inverse, comme indiqué dans le tableau, il y a quelques restrictions mineures sur le format d'entrée de types primitifs JSON qui ne s'appliquent pas aux types PostgreSQL correspondants.
Tableau 8.23. Types primitifs JSON et types PostgreSQL correspondants
Type primitif JSON | Type PostgreSQL | Notes |
---|---|---|
string | text | \u0000 est interdit, comme le sont les
échappements Unicode représentant des caractères non disponibles dans
l'encodage de la base |
number | numeric | Les valeurs NaN et infinity sont interdites |
boolean | boolean | Seules les versions en minuscule de true et false sont acceptées |
null | (none) | NULL dans SQL est un concept différent |
La syntaxe d'entrée/sortie pour les types de données JSON est identique à celle spécifiée dans la RFC 7159.
Les exemples suivants sont tous des expressions json
(ou jsonb
)
valides :
-- Simple valeur scalaire/primitive -- Les valeurs primitives peuvent être des nombres, chaînes entre guillemets, true, false ou null SELECT '5'::json; -- Tableau de zéro ou plus éléments (les éléments doivent être du même type) SELECT '[1, 2, "foo", null]'::json; -- Objets contenant des paires de clé et valeurs -- À noter que les clés d'objets doivent toujours être des chaînes entre guillemets SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json; -- Tableaux et objets peuvent être imbriqués arbitrairement SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;
Comme dit précédemment, quand une valeur JSON est renseignée puis affichée
sans traitement additionnel, json
renvoie le même texte qui
était fourni en entrée, alors que jsonb
ne préserve pas les
détails sémantiquement non significatifs comme les espaces.
Par exemple, il faut noter la différence ici :
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json; json ------------------------------------------------- {"bar": "baz", "balance": 7.77, "active":false} (1 row) SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb; jsonb -------------------------------------------------- {"bar": "baz", "active": false, "balance": 7.77} (1 row)
un détail sémantiquement non significatif qu'il faut souligner est qu'avec
jsonb
, les nombres seront affichés en fonction du type
numeric
sous-jacent. En pratique, cela signifie que les nombres
renseignés avec la notation E
seront affichés sans. Par
exemple :
SELECT '{"reading": 1.230e-5}'::json, '{"reading": 1.230e-5}'::jsonb; json | jsonb -----------------------+------------------------- {"reading": 1.230e-5} | {"reading": 0.00001230} (1 row)
Toutefois, jsonb
préservera les zéros en fin de partie
fractionnaire, comme on peut le voir dans cet exemple, même si ceux-ci
ne sont pas sémantiquement significatifs, pour des besoins tels que des tests
d'égalité.
Pour la liste de fonctions et opérateurs intégrés disponibles pour construire et traiter des valeurs JSON, voir Section 9.16.
Représenter des données en JSON peut être considérablement plus flexible que le modèle de données relationnel traditionnel, qui est contraignant dans des environnements où les exigences sont souples. Il est tout à fait possible que ces deux approches puissent coexister, et qu'elles soient complémentaires au sein de la même application. Toutefois, même pour les applications où on désire le maximum de flexibilité, il est toujours recommandé que les documents JSON aient une structure quelque peu fixée. La structure est typiquement non vérifiée (bien que vérifier des règles métier de manière déclarative soit possible), mais le fait d'avoir une structure prévisible rend plus facile l'écriture de requêtes qui résument utilement un ensemble de « documents » (datums) dans une table.
Les données JSON sont sujettes aux mêmes considérations de contrôle de concurrence que pour n'importe quel autre type de données quand elles sont stockées en table. Même si stocker de gros documents est prévisible, il faut garder à l'esprit que chaque mise à jour acquiert un verrou de niveau ligne sur toute la ligne. Il faut envisager de limiter les documents JSON à une taille gérable pour réduire les contentions sur verrou lors des transactions en mise à jour. Idéalement, les documents JSON devraient chacun représenter une donnée atomique, que les règles métiers imposent de ne pas pouvoir subdiviser en données plus petites qui pourraient être modifiées séparément.
jsonb
#
Tester l'inclusion est une capacité importante de
jsonb
. Il n'y a pas d'ensemble de fonctionnalités parallèles
pour le type json
. L'inclusion teste si un des documents
jsonb
est contenu dans un autre. Ces exemples renvoient vrai,
sauf note explicite :
-- Simple valeur scalaire/primitive qui contient une seule valeur identique : SELECT '"foo"'::jsonb @> '"foo"'::jsonb; -- Le tableau de droite est contenu dans celui de gauche : SELECT '[1, 2, 3]'::jsonb @> '[1, 3]'::jsonb; -- L'ordre des éléments d'un tableau n'est pas significatif, donc ceci est tout -- aussi vrai : SELECT '[1, 2, 3]'::jsonb @> '[3, 1]'::jsonb; -- Les éléments dupliqués d'un tableau n'ont pas plus d'importance : SELECT '[1, 2, 3]'::jsonb @> '[1, 2, 2]'::jsonb; -- L'objet avec une seule paire à droite est contenu -- dans l'objet sur le côté gauche : SELECT '{"product": "PostgreSQL", "version": 9.4, "jsonb":true}'::jsonb @> '{"version":9.4}'::jsonb; -- Le tableau du côté droit n'est <emphasis>pas</emphasis> considéré comme contenu -- dans le tableau du côté gauche, même si un tableau similaire est imbriqué dedans : SELECT '[1, 2, [1, 3]]'::jsonb @> '[1, 3]'::jsonb; -- renvoie faux -- Mais avec une couche d'imbrication, il est contenu : SELECT '[1, 2, [1, 3]]'::jsonb @> '[[1, 3]]'::jsonb; -- De la même manière, l'inclusion n'est pas valable ici : SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"bar": "baz"}'::jsonb; -- renvoie faux -- Une clé du niveau racine et un objet vide sont contenus : SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"foo": {}}'::jsonb;
Le principe général est que l'objet inclus doit correspondre à l'objet devant le contenir à la fois pour la structure et pour les données, peut-être après la suppression d'éléments de tableau ou d'objets paires clé/valeur ne correspondant pas à l'objet contenant. Mais rappelez-vous que l'ordre des éléments dans un tableau n'est pas significatif lors d'une recherche de contenance, et que les éléments dupliqués d'un tableau ne sont réellement considérés qu'une seule fois.
Comme exception qui confirme la règle que les structures doivent correspondre, un tableau peut inclure une valeur primitive :
-- Ce tableau inclut la valeur primitive chaîne : SELECT '["foo", "bar"]'::jsonb @> '"bar"'::jsonb; -- Cette exception n'est pas réciproque, la non-inclusion est rapportée ici : SELECT '"bar"'::jsonb @> '["bar"]'::jsonb; -- renvoie faux
jsonb
a également un opérateur d'existence,
qui est une variation sur le thème de l'inclusion : il teste si une
chaîne (sous forme de valeur text
) apparaît comme une clé
d'objet ou un élément de tableau au niveau supérieur de la valeur
jsonb
. Ces exemples renvoient vrai; sauf note explicite :
-- La chaîne existe comme un élément de tableau : SELECT '["foo", "bar", "baz"]'::jsonb ? 'bar'; -- La chaîne existe comme une clé d'objet : SELECT '{"foo": "bar"}'::jsonb ? 'foo'; -- Les valeurs d'objets ne sont pas examinées : SELECT '{"foo": "bar"}'::jsonb ? 'bar'; -- renvoie faux -- Comme pour l'inclusion, l'existence doit correspondre au niveau supérieur : SELECT '{"foo": {"bar": "baz"}}'::jsonb ? 'bar'; -- renvoie faux -- Une chaîne est examinée pour l'existence si elle correspond à une primitive chaîne JSON : SELECT '"foo"'::jsonb ? 'foo';
Les objets JSON sont plus adaptés que les tableaux pour tester l'inclusion ou l'existence quand il y a de nombreux éléments ou clés impliqués, car contrairement aux tableaux, ils sont optimisés de manière interne pour la recherche et n'ont pas besoin d'être parcourus linéairement.
Comme les documents JSON sont imbriqués, une requête appropriée peut
ignorer une sélection explicite de sous-objets. Par exemple, supposons que
nous ayons une colonne doc
contenant des objets
au plus haut niveau, avec la plupart des objets contenant les champs
tags
qui contiennent eux-mêmes des tableaux de sous-objets. Cette
requête trouve des entrées dans lesquelles les sous-objets contiennent à
la fois "term":"paris"
et
"term":"food"
, tout en ignorant ces clés en dehors du
tableau tags
:
SELECT doc->'site_name' FROM websites WHERE doc @> '{"tags":[{"term":"paris"}, {"term":"food"}]}';
Cela pourrait s'accomplir aussi ainsi :
SELECT doc->'site_name' FROM websites WHERE doc->'tags' @> '[{"term":"paris"}, {"term":"food"}]';
mais cette approche est moins flexible, et souvent bien moins efficace.
Mais l'opérateur JSON d'existence n'est pas imbriqué : il cherchera seulement pour la clé ou l'élément de tableau spécifié à la racine de la valeur JSON.
Les différents opérateurs d'inclusion d'existence, avec tous les autres opérateurs et fonctions JSON, sont documentés dans Section 9.16.
jsonb
#
Les index GIN peuvent être utilisés pour chercher efficacement des clés ou des
paires clé/valeur se trouvant parmi un grand nombre de documents (datums)
jsonb
. Deux « classes d'opérateurs » GIN sont
fournies, offrant différents compromis entre performances et flexibilité.
La classe d'opérateur GIN par défaut pour jsonb
supporte les
requêtes avec des opérateurs clé-existe ?
,
?|
et ?&
et l'opérateur de
contenance @>
et les opérations de correspondance
jsonpath
@?
et @@
. (Pour
des détails sur la sémantique que ces opérateurs implémentent, voir Tableau 9.46.) Un exemple de création d'index avec
cette classe d'opérateurs est :
CREATE INDEX idxgin ON api USING GIN (jdoc);
La classe d'opérateurs GIN qui n'est pas par défaut jsonb_path_ops
n'accepte pas les opérateurs clé-existe, mais accepte
@>
, @?
et @@
.
seulement. Un exemple de création d'index avec cette classe d'opérateurs
est :
CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops);
En étudiant l'exemple d'une table qui stocke des documents JSON récupérés par un service web tiers, avec une définition de schéma documentée, un document typique serait :
{ "guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a", "name": "Angela Barton", "is_active": true, "company": "Magnafone", "address": "178 Howard Place, Gulf, Washington, 702", "registered": "2009-11-07T08:53:22 +08:00", "latitude": 19.793713, "longitude": 86.513373, "tags": [ "enim", "aliquip", "qui" ] }
Ces documents sont stockés dans une table nommée api
,
dans une colonne de type jsonb
nommée jdoc
.
Si un index GIN est créé sur cette colonne, des requêtes semblables à
l'exemple suivant peuvent utiliser cet index :
-- Trouver les documents dans lesquels la clé "company" a pour valeur "Magnafone" SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"company": "Magnafone"}';
Toutefois, cet index ne pourrait pas être utilisé pour des requêtes comme
dans l'exemple suivant, car bien que l'opérateur ?
soit
indexable, il n'est pas appliqué directement sur la colonne indexée
jdoc
:
-- Trouver les documents dans lesquels la clé "tags" contient une clé ou un élément tableau "qui" SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc -> 'tags' ? 'qui';
Toutefois, avec l'utilisation appropriée d'index sur expression, la requête
ci-dessus peut utiliser un index. Si le requêtage d'éléments particuliers
de la clé "tags"
est fréquent, définir un index comme
ceci pourrait être particulièrement bénéfique :
-- À noter que l'opérateur "jsonb -> text" ne peut être appelé que sur un -- objet JSON, donc la conséquence de créer cet index est que le premier niveau de -- chaque valeur "jdoc" doit être un objet. Ceci est vérifié lors de chaque insertion. CREATE INDEX idxgintags ON api USING GIN ((jdoc -> 'tags'));
Dorénavant, la clause WHERE
jdoc -> 'tags' ? 'qui'
sera reconnue comme une
application de l'opérateur indexable ?
pour l'expression
indexée jdoc -> 'tags'
.
(Plus d'informations sur les index sur expression peuvent être
trouvées dans Section 11.7.)
Une autre approche pour le requêtage et l'exploitation de l'inclusion, par exemple :
-- Trouver les documents dans lesquels la clé "tags" inclut l'élément tableau "qui" SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qui"]}';
Un simple index GIN sur la colonne jdoc
peut
répondre à cette requête. Mais il faut noter qu'un tel index stockera des
copies de chaque clé et chaque valeur de la colonne
jdoc
, alors que l'index sur expression de
l'exemple précédent ne stockera que les données trouvées pour la clé
tags
. Alors que l'approche d'index simple est bien plus
souple (puisqu'elle supporte les requêtes sur n'importe quelle clé), les
index sur des expressions ciblées ont bien plus de chances d'être plus petits
et plus rapides pour la recherche qu'un simple index.
Les index GIN supportent aussi les opérateurs @?
et
@@
qui réalisent la correspondance
jsonpath
. Voici des exemples :
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @@ '$.tags[*] == "qui"';
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @? '$.tags[*] ? (@ == "qui")';
Pour ces opérateurs, l'index GIN extrait les clauses de la forme suivante du
motif jsonpath
: chaîne des
accesseurs
== constante
et effectue
la recherche d'index en se basant sur les clés et valeurs mentionnées dans
ces clauses. La chaîne des accesseurs peut être les accesseurs
.key
, [*]
, et
[
. La classe d'opérateur
index
]jsonb_ops
accepte aussi les accesseurs
.*
et .**
, contrairement à la classe
d'opérateur jsonb_path_ops
.
Bien que la classe d'opérateur jsonb_path_ops
ne supporte
que les requêtes avec les opérateurs @>
, @?
et @@
, elle a des
avantages de performances notables par rapport à la classe d'opérateur par
défaut jsonb_ops
. Un index jsonb_path_ops
est généralement bien plus petit qu'un index jsonb_ops
pour les mêmes données, et la spécificité de la recherche est meilleure,
particulièrement quand les requêtes contiennent des clés qui apparaissent
fréquemment dans les données. Par conséquent, les opérations de recherche
sont généralement plus performantes qu'avec la classe d'opérateur par défaut.
La différence technique entre des index GIN jsonb_ops
et
jsonb_path_ops
est que le premier crée des éléments
d'index indépendants pour chaque clé et valeur dans les données, alors
que le second crée des éléments d'index uniquement pour chaque valeur
dans les données.
[7]
Fondamentalement, chaque élément d'index jsonb_path_ops
est un hachage de la valeur et de la ou des clés y menant ; par exemple pour
indexer {"foo": {"bar": "baz"}}
, un seul élément dans
l'index sera créé, incorporant les trois foo
, bar
et baz
dans une valeur hachée. Ainsi, une requête
d'inclusion cherchant cette structure résulterait en une recherche d'index
extrêmement spécifique, mais il n'y a pas d'autre moyen de savoir si
foo
apparaît en tant que clé. D'un autre côté, un index
jsonb_ops
créerait trois éléments d'index représentant
foo
, bar
et baz
séparément ; ainsi, pour faire la requête d'inclusion, il faudrait rechercher
les lignes contenant chacun des trois éléments. Bien que les index GIN
puissent effectuer de telles recherches et de manière tout à fait efficace,
cela sera toujours moins spécifique et plus lent que la recherche
équivalente jsonb_path_ops
, surtout s'il y a un très
grand nombre de lignes contenant n'importe lequel des trois éléments d'index.
Un désavantage de l'approche jsonb_path_ops
est qu'elle ne
produit d'entrées d'index que pour les structures JSON ne contenant aucune
valeur, comme {"a": {}}
. Si une recherche pour des
documents contenant une telle structure est demandée, elle nécessitera un
parcours de la totalité de l'index, ce qui peut être assez long.
jsonb_path_ops
est donc mal adapté pour des applications
qui effectuent souvent de telles recherches.
jsonb
supporte également les index btree
et
hash
. Ceux-ci ne sont généralement utiles que s'il est
important de vérifier l'égalité de documents JSON entiers.
Le tri btree
pour des données jsonb
est
rarement d'un grand intérêt, mais afin d'être exhaustif, il est :
Objet
>Tableau
>Booléen
>Nombre
>Chaîne
>Null
Objet avec n paires
>objet avec n - 1 paires
Tableau avec n éléments
>tableau avec n - 1 éléments
Les objets avec le même nombre de paires sont comparés dans cet ordre :
clé-1
,valeur-1
,clé-2
...
À noter que les clés d'objet sont comparées dans leur ordre de stockage ; en particulier, puisque les clés les plus courtes sont stockées avant les clés les plus longues, cela peut amener à des résultats contre-intuitifs, tels que :
{ "aa": 1, "c": 1} > {"b": 1, "d": 1}
De la même manière, les tableaux avec le même nombre d'éléments sont comparés dans l'ordre :
élément-1
,élément-2
...
Les valeurs JSON primitives sont comparées en utilisant les mêmes règles de comparaison que pour les types de données PostgreSQL sous-jacents. Les chaînes sont comparées en utilisant la collation par défaut de la base de données.
jsonb
#
Le type de données jsonb
accepte les expressions avec indice,
du style tableau, pour extraire et modifier les éléments. Les valeurs
imbriquées peuvent être indiquées en chaînant les expressions à indice, en
suivant les mêmes règles que l'argument path
dans la
fonction jsonb_set
. Si une valeur jsonb
est un tableau, les indices numériques commencent à zéro, et les nombres
négatifs comptent àpartir du dernier élément du tableau. Les expressions
d'intervalle ne sont pas acceptées. Le résultat d'une expression par
indice est toujours du type de données jsonb.
Les requêtes UPDATE
peuvent utiliser l'indiçage dans la
clause SET
pour modifier les valeurs
jsonb
. Les chemins d'indice doivent être parcourables pour
toutes les valeurs affectées. Par exemple, le chemin val['a']
['b']['c']
peut être entièrement traversé jusqu'à
c
si val
, val
['a']
et val['a']['b']
sont des objets. Si
val['a']
ou val['a']['b']
ne sont
pas définis, il sera créé comme un objet vide et rempli comme nécessaire.
Néanoins, si val
lui-même ou une des valeurs
intermédiaires est défini comme un non-objet (une chaîne, un nombre ou le
null
jsonb
), la traversée ne peut
pas continuer, donc une erreur est levée et la transaction est annulée.
Un exemple de syntaxe avec indiçage :
-- Extraire la valeur d'un objet par sa clé SELECT ('{"a": 1}'::jsonb)['a']; -- Extraire une valeur d'objet imbriqué par le chemin de la clé SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c']; -- Extraire l'élément du tableau par indice SELECT ('[1, "2", null]'::jsonb)[1]; -- Mise à jour de la valeur de l'objet par sa clé. -- Notez les guillemets simples autour de '1' : la valeur affectée -- doit aussi être du type jsonb UPDATE table_name SET jsonb_field['key'] = '1'; -- Ceci lèvera une erreur si un enregistrement de jsonb_field['a']['b'] se révèle -- être autre chose qu'un objet. Par exemple, la valeur {"a": 1} a une valeur numérique -- de la clé 'a'. UPDATE table_name SET jsonb_field['a']['b']['c'] = '1'; -- Filtrer lesenregistrements en utilisant une clause WHERE avec des indices. -- Comme le résultat de l'indiçage est jsonb, la valeur à laquelle nous la comparons -- doit aussi être jsonb. -- Les guillemets doubles rendent valide "value" comme chaîne jsonb. SELECT * FROM table_name WHERE jsonb_field['key'] = '"value"';
L'affectation de jsonb
via l'indiçage gère quelques cas particuliers
différemment de jsonb_set
. Quand une valeur source jsonb
vaut NULL
, l'affectation via l'indiçage continuera comme s'il s'agissait d'une
valeur JSON vide (objet ou tableau) impliquée par la clé d'indiçage :
-- Là où jsonb_field valait NULL, il vaut maintenant {"a": 1} UPDATE table_name SET jsonb_field['a'] = '1'; -- Là où jsonb_field valait NULL, il vaut maintenant [1] UPDATE table_name SET jsonb_field[0] = '1';
Si un index est indiqué pour un tableau contenant trop peu d'éléments, les
éléments NULL
seront ajoutés jusqu'à ce que l'index
soit atteignable et que la valeur puisse être initialisée.
-- Là où jsonb_field valait [], il vaut maintenant [null, null, 2]; -- Là où jsonb_field valait [0], il vaut maintenant [0, null, 2] UPDATE table_name SET jsonb_field[2] = '2';
Une valeur de type jsonb
acceptera des affectations sur des
chemins indicés non existants car le dernier élément existant à être
traversé est un objet ou un tableau, comme impliqué par l'indice
correspondant (l'élément indiqué par le dernier indice dans le chemin
n'est pas traversé et pourrait être n'improte quoi). Le tableau imbriqué
et les structures d'objet seront créés et, dans le premier cas, aligné
avec des null
, comme spécifié par le chemin d'indice
jusqu'à ce que la valeur affectée puisse être placée.
-- Là où jsonb_field valait {}, il vaut maintenant {"a": [{"b": 1}]} UPDATE table_name SET jsonb_field['a'][0]['b'] = '1'; -- Là où jsonb_field valait [], il vaut maintenant [null, {"a": 1}] UPDATE table_name SET jsonb_field[1]['a'] = '1';
Des extensions supplémentaires sont disponibles pour implémenter des
transformations pour le type jsonb
pour différents langages de
procédure stockée.
Les extensions pour PL/Perl sont appelées jsonb_plperl
et jsonb_plperlu
. Si vous les utilisez, les valeurs
jsonb
sont transformées en tableaux, hachages et scalaires
Perl, suivant le cas.
L'extension pour PL/Python est appelée jsonb_plpython3u
.
Si vous l'utilisez, les valeurs jsonb
sont transformées en
dictionnaires, listes et scalaires Python, suivant le cas.
Pour ces extensions, jsonb_plperl
est considéré de
confiance (« trusted »), autrement dit il peut être installé par
des utilisateurs qui n'ont pas l'attribut SUPERUSER
mais
qui ont le droit CREATE
sur la base actuelle. Le reste
nécessite d'être superutilisateur pour être installé.
Le type jsonpath
implémente le support du langage
SQL/JSONpath dans PostgreSQL de façon à requêter
correctement dans les données JSON.
Cela fournit une représentation binaire de l'expression SQL/JSONpath analysée
qui spécifie les valeurs qui doivent être récupérées par le moteur de chemin
depuis les données JSON pour être traités par la suite avec les fonctions
de requêtage SQL/JSON.
La sémantique des prédicats et opérateurs suit généralement celle du SQL. Dans le même temps, il propose une manière naturelle de travailler avec les données JSON. En effet, la syntaxe SQL/JSONpath utilise certaines conventions JavaScript :
Le point (.
) est utilisé pour accéder à une valeur.
Les crochets ([]
) sont utilisés pour accéder à un tableau.
Les tableaux SQL/JSON commencent à partir de 0, contrairement aux tableaux SQL classiques qui commencent à 1.
Les numériques dans les expressions de SQL/JSON path suivent les règles
de JavaScript, qui sont différentes des règles du SQL et du JSON sur des
détails mineures. Par exemple, SQL/JSON path autorise
.1
et 1.
, qui sont invalides en JSON.
Les entiers non décimaux et les séparateurs sous la forme de tirets bas
sont acceptés, par exemple
1_000_000
, 0x1EEE_FFFF
,
0o273
, 0b100101
. Dans SQL/JSON path
(et dans JavaScript, mais pas dans SQL), il ne doit pas y avoir un séparateur
tiret bas après le préfixe.
Une expression de chemin SQL/JSON est typiquement écrite dans une requête
SQL comme une constante chaîne de caractères SQL, donc elle doit être
entourée par des guillemets simples, et tout guillemet simple souhaité dans
la valeur doit être doublé (voir Section 4.1.2.1).
Certaines formes d'expressions de chemin requièrent des constantes de
chaîne à l'intérieur. Ces constantes de chaînes intégrées suivent les
conventions JavaScript/ECMAScript : elles doivent être entourées par
des guillemets doubles et les échappements doivent être utilisées pour
représenter les caractères difficiles à saisir. En particulier, la façon
d'écrire un guillemet double dans une constante de type chaîne est
\"
, et pour écrire un antislash, vous devez écrire
\\
. Les autres séquences spéciales d'échappement
incluent celles reconnues dans les chaînes JavaScript :
\b
, \f
, \n
,
\r
, \t
, \v
pour
les différents caractères de contrôle ASCII,
\x
pour un code caractère
écrit avec deux chiffres hexadécimaux,
NN
\u
pour un point code
caractère Unicode écrit avec 1 à 6 chiffres hexadécimaux.
NNNN
Une expression path consiste en une séquence d'éléments path, qui peuvent être une des suivants :
Path litéral de type primitifs JSON : texte Unicode, numeric, true, false, ou null.
Variables Path listées dans Tableau 8.24.
Opérateurs d'accès listés dans Tableau 8.25.
Opérateurs et méthodes jsonpath
listés dans
Section 9.16.2.3.
Les parenthèses, qui peuvent être utilisées pour filtrer des expressions ou définir l'ordre d'évaluation des Path.
Pour plus de détails concernant l'utilisation des expressions jsonpath
avec des fonctions de requête SQL/JSON, voir Section 9.16.2.
Tableau 8.24. Variables jsonpath
Variable | Description |
---|---|
$ | Une variable qui représente le texte JSON en cours de traitement (l'élément de contexte). |
$varname |
Une variable nommée. Le paramètre vars présent dans
de nombreuses fonctions de traitement JSON, permet de définir sa valeur.
Voir Tableau 9.49
pour plus de détails.
|
@ | Une variable qui représente le résultat de l'évaluation du path dans un filtre. |
Tableau 8.25. Accesseurs jsonpath
Opérateurs d'accès | Description |
---|---|
|
La valeur accédée renvoie un objet avec la clé spécifiée.
Si le nom de la clé correspond à certaines variables nommées commençant avec
|
|
L'accès joker renvoie les valeurs de tous les membres se trouvant au plus haut niveau de l'objet courant. |
|
L'accès joker récursif traite tous les niveaux de l'arborescence JSON de l'objet courant, et retourne toutes les valeurs, peu importe leur niveau d'imbrication. Ceci est une extension PostgreSQL du standard SQL/JSON |
|
Identique à |
|
Accès aux valeurs d'un tableau.
L' |
|
L'accès joker tableau qui renvoie toutes les valeurs du tableau. |
[7] Dans ce contexte, le terme « valeur » inclut les éléments de tableau, bien que la terminologie JSON considère parfois que les éléments de tableaux soient distincts des valeurs dans les objets.