Cette section décrit :
les fonctions et opérateurs pour traiter et créer des données JSON
le langage de chemin SQL/JSON
les fonctions de requêtage SQL/JSON
Pour fournir un support natif des types de données JSON dans un environnement SQL, PostgreSQL implémente le modèle de données SQL/JSON. Ce modèle comprends des séquences d'éléments. Chaque élément peut contenir des valeurs SQL scalaires, avec une valeur null SQL/JSON supplémentaire, et des structures de données composites qui utilisent les tableaux et objets JSON. Le modèle est une formalisation du modèle de données compris dans la spécification JSON de la RFC 7159.
SQL/JSON vous permet de gérer des données JSON parmi les données SQL habituelles avec un support des transactions, incluant :
Le chargement de données JSON dans la base de données et son stockage dans des colonnes SQL standards, tout comme des chaînes de caractères ou des chaînes binaires.
La génération d'objets JSON et de tableaux à partir de données relationnelles.
Le requêtage de données JSON en utilisant les fonctions de requêtage SQL/JSON et les expressions du langage de chemin SQL/JSON.
Pour en savoir plus sur le standard SQL/JSON, voir [sqltr-19075-6]. Pour des détails sur les types JSON supportés dans PostgreSQL, voir Section 8.14.
Tableau 9.45 affiche les opérateurs
disponibles pour les types de données JSON (voir Section 8.14). De plus, les opérateurs de comparaison
habituels indiqués dans Tableau 9.1
sont disponibles pour le type jsonb
, mais pas pour le type
json
. Les opérateurs de comparaison suivent les règles de tri
des opérateurs B-tree indiqués dans Section 8.14.4.
Voir aussi Section 9.21 pour la fonction
d'agrégat json_agg
qui agrège les valeurs
d'enregistrements sous la forme d'un JSON, pour la fonction d'agrégat
json_object_agg
qui agrège des paires de valeurs dans
un objet JSON, et leurs équivalents jsonb
, à savoir
jsonb_agg
et jsonb_object_agg
.
Tableau 9.45. Opérateurs json
et jsonb
Opérateur Description Exemple(s) |
---|
Extrait le
|
Extrait le champ objet JSON avec la clé donnée.
|
Extrait le
|
Extrait le champ objet JSON d'après la clé donnée, comme
|
Extrait le sous-objet JSON d'un chemin spécifié, où les éléments du chemin peuvent être soit les clés d'un champ, soit les indices d'un tableau.
|
Extrait le sous-objet JSON au chemin spécifié avec
|
Les opérateurs d'extraction champ/élément/chemin renvoient NULL, plutôt qu'une erreur, si l'entrée JSON n'a pas la bonne structure correspondant à la requête ; par exemple si la clé ou l'élément du tableau n'existe pas.
Quelques opérateurs supplémentaires existent seulement pour le type
jsonb
, comme indiqué dans Tableau 9.46. Section 8.14.4
décrit comment ces opérateurs peuvent être utilisés pour rechercher
efficacement les données jsonb
indexées.
Tableau 9.46. Opérateurs jsonb
supplémentaires
Opérateur Description Exemple(s) |
---|
Est-ce que la première valeur JSON contient la seconde ? (Voir Section 8.14.3 pour les détails sur la notion de contenu.)
|
Est-ce que la première valeur JSON est contenue dans la seconde ?
|
Est-ce que la chaîne de caractères existe comme clé de haut niveau ou élément de tableau dans la valeur JSON ?
|
Est-ce qu'une des chaînes du tableau de texte existe comme clé de haut niveau ou comme élément de tableau ?
|
Est-ce que toutes les chaînes du tableau de texte existent comme clés de haut niveau ou comme éléments de tableau ?
|
Concatène deux valeurs
Pour concaténer un tableau à un autre tableau dans une entrée unique, il faut l'envelopper dans une couche supplémentaire de tableau, par exemple :
|
Supprime une clé (et sa valeur) à partir d'un objet JSON, ou les valeurs correspondantes de chaînes à partir d'un tableau JSON.
|
Supprime toutes les clés ou tous les éléments de tableau correspondant à partir de l'opérande gauche.
|
Supprime l'élément de tableau pour l'index spécifié (les nombres négatifs se décomptent à partir de la fin). Renvoie une erreur si la valeur JSON n'est pas un tableau.
|
Supprime le champ ou l'élément de tableau sur le chemin indiqué, les éléments du chemin pouvant être soit des clés de champ, soit des indices de tableau.
|
Est-ce que le chemin JSON renvoie tout élément pour la valeur JSON spécifiée ? (Ceci est utile seulement avec les expressions de chemin JSON du standard SQL, pas avec les expressions de vérification du prédicat, car ces dernières renvoient toujours une valeur.)
|
Renvoie le résultat d'une vérification de prédicat du chemin JSON pour
la valeur JSON indiquée.
(Cela n'est utile qu'avec
les expressions
de vérification de prédicat, et non avec les expressions de chemin
JSON du standard SQL, car il renverra
|
Les opérateurs @?
et @@
du type
jsonpath
suppriment les erreurs suivantes : champ objet
ou élément de tableau manquant, type d'élément JSON inattendu, erreurs de
date/heure ou de numérique. Les fonctions relatives à
jsonpath
décrites ci-dessous peuvent aussi supprimer ce type
d'erreurs à la demande. Ce comportement pourrait être utile lors de la
recherche de collections de documents JSON de différentes structures.
Tableau 9.47 montre les fonctions
disponibles pour construire des valeurs json
et
jsonb
. Certaines fonctions de cette table ont une clause
RETURNING
, qui indique le type de données renvoyées.
Il doit être parmi json
, jsonb
,
bytea
, un type chaîne de caractères (text
,
char
, varchar
ou nchar
), ou un
type qui peut être converti en json
. Par défaut, le type
json
est renvoyé.
Tableau 9.47. Fonctions de création de JSON
Fonction Description Exemple(s) |
---|
Convertit toute valeur SQL en
|
Convertit un tableau SQL en tableau JSON. Le comportement est le même
que
|
Construit un tableau JSON à partir d'une série de paramètres
|
Convertit une valeur composite SQL en objet JSON. Le comportement est
le même que
|
Construit un tableau JSON avec des types potentiellement hétérogènes à
partir d'une liste d'arguments de longueur variable. Chaque argument
est converti avec la fonction
|
Construit un objet JSON à partir d'une liste d'arguments de longueur
indéfinie. Par convention, la liste d'arguments consiste en des clés
et valeurs en alternance. Les arguments clés sont convertis en
texte ; les arguments valeurs sont convertis via les fonctions
|
Construit un objet avec toutes les paires clé/valeur données ou un
objet vide si aucune n'est donnée.
|
Construit un objet JSON à partir d'un tableau de texte. Le tableau doit avoir soit exactement une dimension avec un nombre pair de membres, auquel cas ils sont pris comme des paires alternantes clé/valeur, ou deux dimensions de telle façon que le tableau interne a exactement deux éléments, pris comme une paire clé/valeur. Toutes les valeurs sont converties en chaîne JSON.
|
Cette forme de
|
Convertit une expression donnée spécifiée comme une chaîne
|
Convertit une valeur scalaire SQL donnée en une valeur scalaire JSON. Si l'entrée est NULL, une valeur NULL SQL est renvoyée. Si l'entrée est un nombre ou une valeur booléenne, une valeur numérique ou booléenne correspondante en JSON est renvoyée. Pour toute autre valeur, une chaîne JSON est renvoyée.
|
Convertit une expression SQL/JSON en une chaîne de caractères ou en une chaîne binaire.
L'
|
Tableau 9.48 détaille les possibilités de SQL/JSON pour tester du JSON.
Tableau 9.48. Fonctions de test SQL/JSON
Tableau 9.49 montre les fonctions
disponibles pour le traitement de valeurs json
et
jsonb
.
Tableau 9.49. Fonctions de traitement JSON
Fonction Description Exemple(s) |
---|
Étend le tableau JSON de haut niveau en un ensemble de valeurs JSON.
value ----------- 1 true [2,false]
|
Étend le tableau JSON de haut niveau en un ensemble de valeurs de type
value ----------- foo bar
|
Renvoie le nombre d'éléments dans le tableau JSON de haut niveau.
|
Étend l'objet JSON de haut niveau en un ensemble de paires clé/valeur.
key | value -----+------- a | "foo" b | "bar"
|
Étend l'objet JSON de haut niveau en un ensemble de paires clé/valeur.
Les
key | value -----+------- a | foo b | bar
|
Extrait un sous-objet JSON au chemin spécifié. (Ceci est
fonctionnellement équivalent à l'opérateur
|
Extrait le sous-objet JSON au chemin spécifié sous la forme d'un
|
Renvoie l'ensemble de clés dans l'objet JSON de haut niveau.
json_object_keys ------------------ f1 f2
|
Étend l'objet JSON de haut niveau en une ligne ayant le type composite
de l'argument Pour convertir une valeur JSON au type SQL d'une colonne en sortie, les règles suivantes sont appliquées sur cette séquence :
Bien que l'exemple ci-dessous utilise une valeur constante JSON, une
utilisation typique serait de référencer une colonne
a | b | c ---+-----------+------------- 1 | {2,"a b"} | (4,"a b c")
|
Fonction pour tester
jsonb_populate_record_valid ----------------------------- f (1 row)
ERROR: value too long for type character(2)
jsonb_populate_record_valid ----------------------------- t (1 row)
a ---- aa (1 row)
|
Étend le tableau JSON de haut niveau composé d'objets en un ensemble
de lignes ayant le type composite de l'argument
a | b ---+--- 1 | 2 3 | 4
|
Étend l'objet JSON de haut niveau en une ligne ayant le type composite
défini par une clause
a | b | c | d | r ---+---------+---------+---+--------------- 1 | [1,2,3] | {1,2,3} | | (123,"a b c")
|
Étend le tableau JSON de haut niveau composé d'objets en un ensemble
de lignes ayant le type composite défini par une clause
a | b ---+----- 1 | foo 2 |
|
Renvoie
|
Si
|
Renvoie
|
Supprime tous les champs objets ayant des valeurs NULL à partir de la valeur JSON donnée, de façon récurvise. Les valeurs NULL qui ne sont pas des champs objets ne sont pas touchées.
|
Vérifie si le chemin JSON renvoie des éléments de la valeur JSON
spécifiée.
(Cela n'est utile qu'avec les expressions de chemin JSON standard SQL, et non avec les
expressions de vérification de prédicat,
car celles-ci renvoient toujours une valeur.)
Si l'argument
|
Renvoie le résultat de la vérification d'un prédicat de chemin JSON
pour la valeur JSON spécifiée.
(Cela n'est utile qu'avec les expressions
de vérification de prédicat, et non avec les expressions
de chemin JSON standard SQL, car cela échouera ou renverra
|
Renvoie tous les éléments JSON renvoyés par le chemin JSON pour la
valeur JSON spécifiée. Pour les expressions de chemin JSON standard
SQL, il renvoie les valeurs JSON sélectionnées à partir de
jsonb_path_query ------------------ 2 3 4
|
Renvoie tous les éléments JSON renvoyés par le chemin JSON pour la
valeur JSON spécifiée, sous la forme d'un tableau JSON.
Les paramètres sont les mêmes que
pour
|
Renvoie le premier élément JSON renvoyé par le chemin JSON pour la
valeur JSON spécifiée ou
|
Ces fonctions agissent comme leurs homologues décrits ci-dessus sans
le suffixe
|
Convertit la valeur JSON donnée en un texte proprement indenté.
[ { "f1": 1, "f2": null }, 2 ]
|
Renvoie le type de la valeur JSON de haut niveau sous la forme d'une
chaîne de caractères. Les types possibles sont
|
Les expressions de chemin SQL/JSON indiquent les éléments à récupérer à
partir de données JSON, de façon similaire aux expressions XPath utilisées pour
l'accès SQL au XML. Dans PostgreSQL, les
expressions de chemin sont implémentées sous la forme du type de données
jsonpath
et peuvent utiliser tout élément décrit dans Section 8.14.7.
Les fonctions et opérateurs de requêtes JSON passent l'expression de chemin
fournie au moteur de chemin pour évaluation. Si
l'expression correspond à la donnée JSON requêtée, l'élément JSON
correspondant ou l'ensemble d'éléments est renvoyé.
S'il n'y a pas de correspondance, le résultat sera NULL
,
false
, ou une erreur, selon la fonction.
Les expressions de
chemin sont écrites dans le langage de chemin SQL/JSON et peuvent inclure
les expressions et fonctions arithmétiques.
Une expression de chemin consiste en une séquence d'éléments autorisés par
le type de données jsonpath
. L'expression de chemin est
habituellement évaluée de gauche à droite, mais vous pouvez utiliser les
parenthèses pour modifier l'ordre des opérations. Si l'évaluation réussit,
une séquence d'éléments JSON est produite et le résultat de l'évaluation
est renvoyé à la fonction de requête JSON qui termine le traitement
indiqué.
Pour faire référence à la valeur JSON en cours de requêtage
(l'élément de contexte), utilisez la variable
$
dans l'expression de chemin. Le premier élement du
chemin doit toujours être $
. Elle peut être suivie par
un ou plusieurs opérateurs
d'accesseurs, qui descendent dans la structure JSON, étape par
étape, pour récupérer les sous-éléments de l'élément de contexte. Chaque
opérateur d'accès agit sur le(s) résultat(s) de l'étape d'évaluation
précédente, produisant zéro, un ou plusieurs éléments de sortie
à partir de chaque élément d'entrée.
Par exemple, supposez que vous ayez certaines données JSON à partir d'un traqueur GPS que vous voulez analyser, tel que :
SELECT '{ "track": { "segments": [ { "location": [ 47.763, 13.4034 ], "start time": "2018-10-14 10:05:14", "HR": 73 }, { "location": [ 47.706, 13.2635 ], "start time": "2018-10-14 10:39:21", "HR": 135 } ] } }' AS json \gset
(L'exemple ci-dessus peut être copié-collé dans
psql pour préparer les exemples suivants.
Ensuite, psql remplacera
:'json'
par une constante de chaîne de caractère,
contenant la valeur JSON.)
Pour récupérer les segments de piste disponibles, vous avez besoin
d'utiliser l'opérateur d'accesseur
.
pour descendre à travers
les objets JSON tout autour, par exemple :
key
=>
select jsonb_path_query(:'json', '$.track.segments');
jsonb_path_query ------------------------------------------------------------------------------------------------------------------------------------------------------------------- [{"HR": 73, "location": [47.763, 13.4034], "start time": "2018-10-14 10:05:14"}, {"HR": 135, "location": [47.706, 13.2635], "start time": "2018-10-14 10:39:21"}]
Pour récupérer le contenu d'un tableau, vous utilisez typiquement
l'opérateur [*]
. L'exemple suivant renverra
les coordonnées d'emplacement pour tous les segments de piste
disponibles :
=>
select jsonb_path_query(:'json', '$.track.segments[*].location');
jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635]
Ici, nous avons commencé avec l'intégralité de la valeur d'entrée JSON
($
), puis l'opérateur d'accès .track
a sélectionné l'objet JSON associé à la clé d'objet
"track"
, ensuite l'opérateur d'accès
.segments
a sélectionné le tableau JSON associé à la clé
"segments"
dans cet objet, puis l'opérateur d'accès
[*]
a sélectionné chaque élément de ce tableau
(produisant une série d'éléments), enfin l'opérateur d'accès
.location
a sélectionné le tableau JSON associé à la clé
"location"
dans chacun de ces objets. Dans cet exemple,
chacun de ces objets avait une clé "location"
; maissi
si l'un d'eux ne l'avait pas, l'opérateur d'accès .location
n'aurait pas donnée d'élement de sortie pour cet élément d'entrée.
Pour renvoyer les coordonnées du premier segment seulement, vous pouvez
indiquer l'indice correspondant dans l'opérateur []
.
Rappellez-vous que les indices de tableau JSON commencent à 0 :
=>
select jsonb_path_query(:'json', '$.track.segments[0].location');
jsonb_path_query ------------------- [47.763, 13.4034]
Le résultat de chaque étape d'évaluation de chemin peut être traité par un
ou plusieurs opérateurs et méthodes jsonpath
, listés dans
Section 9.16.2.3. Chaque nom de méthode
doit être précédé d'un point. Par exemple, vous pouvez obtenir la taille
d'un tableau :
=>
select jsonb_path_query(:'json', '$.track.segments.size()');
jsonb_path_query ------------------ 2
D'autres exemples d'utilisation d'opérateurs et méthodes
jsonpath
à l'intérieur d'expressions de chemins apparaissent
ci-dessous dans Section 9.16.2.3.
Un chemin peut aussi contenir
plusieurs expressions de filtre qui fonctionnent de
façon similaire à la clause WHERE
en SQL. Une expression
de filtre commence avec un point d'interrogation et fournit une condition
entre parenthèses :
? (condition
)
Les expressions de filtre doivent être écrites juste après l'étape
d'évaluation du chemin auquel elles s'appliquent. Le résultat de cette
étape est filtré pour inclure seulement les éléments qui satisfont la
condition fournie. SQL/JSON définit une logique à trois valeurs, donc la
condition peut produire true
, false
ou
unknown
. La valeur unknown
joue le
même rôle que le NULL
SQL et peut être testée avec le
prédicat is unknown
. Les étapes suivantes d'évaluation
du chemin utilisent seulement les éléments pour lesquels l'expression de
filtre renvoie true
.
Les fonctions et opérateurs pouvant être utilisés dans des expressions de
filtre sont listés dans Tableau 9.51. À l'intérieur d'une
expression de filtre, la variable @
dénote la valeur en
cours de considération (un résultat de l'étape de chemin précédente). Vous
pouvez écrire les opérateurs d'accesseurs après @
pour
récupérer les éléments du composant.
Par exemple, supposez que vous vouliez récupérer toutes les valeurs de fréquence cardiaque supérieures à 130. Vous pouvez le faire ainsi :
=>
select jsonb_path_query(:'json', '$.track.segments[*].HR ? (@ > 130)');
jsonb_path_query ------------------ 135
Pour obtenir les heures de début des segments ayant une telle valeur, vous devez filtrer les segments incompatibles avant de sélectionner les heures de début, donc l'expression de filtre est appliquée à l'étape précédente, et le chemin utilisé dans la condition est différent :
=>
select jsonb_path_query(:'json', '$.track.segments[*] ? (@.HR > 130)."start time"');
jsonb_path_query ----------------------- "2018-10-14 10:39:21"
Vous pouvez utiliser plusieurs expressions de filtre en séquence, si nécessaire. L'expression suivante sélectionne les heures de début de tous les segments qui contiennent des emplacements avec les bonnes coordonnées et les valeurs hautes de fréquence cardiaque :
=>
select jsonb_path_query(:'json', '$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"');
jsonb_path_query ----------------------- "2018-10-14 10:39:21"
Il est aussi autorisé d'utiliser des expressions de filtre à différents niveaux de profondeur. L'exemple suivant filtre tout d'abord tous les segments par emplacement, puis renvoie les valeurs hautes de fréquence cardiaque sur ces segments, si disponibles :
=>
select jsonb_path_query(:'json', '$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)');
jsonb_path_query ------------------ 135
Vous pouvez aussi imbriquer les expressions de filtre. Cet exemple renvoie la taille du chemin s'il contient des segments ayant des valeurs de fréquence cardiaque élevées ou une séquence vide sinon :
=>
select jsonb_path_query(:'json', '$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()');
jsonb_path_query ------------------ 2
L'implémentation PostgreSQL du langage de chemin SQL/JSON présente les différences suivantes sur le standard SQL/JSON :
Étant une extension du standard SQL, une expression de chemin
PostgreSQL peut être un prédicat booléen,
alors que le standard SQL autorise les prédicats uniquement dans les
filtres. Tandis que les expressions de chemin conformes au standard SQL
renvoient les éléments pertinents de la valeur JSON interrogée, les
expressions de vérification de prédicat renvoient le résultat unique du
prédicat avec trois valeurs possibles : true
,
false
ou unknown
. Par exemple, nous
pourrions écrire cette expression de filtre qui est conforme au standard
SQL :
=>
select jsonb_path_query(:'json', '$.track.segments ?(@[*].HR > 130)');
jsonb_path_query --------------------------------------------------------------------------------- {"HR": 135, "location": [47.706, 13.2635], "start time": "2018-10-14 10:39:21"}
L'expression de vérification de prédicat similaire renvoie simplement
true
, indiquant qu'une correspondance existe :
=>
select jsonb_path_query(:'json', '$.track.segments[*].HR > 130');
jsonb_path_query ------------------ true
Les expressions de vérification de prédicat sont requises
dans l'opérateur @@
(et la fonction
jsonb_path_match
), et ne doivent pas être utilisées
avec l'opérateur @?
(ou la fonction
jsonb_path_exists
).
Il existe de légères différences dans l'interprétation des modèles
d'expressions régulières utilisés dans les filtres like_regex
,
comme décrit dans Section 9.16.2.4.
Quand vous requêtez des données JSON, l'expression de chemin pourrait ne pas correspondre à la structure de données JSON actuelle. Une tentative d'accéder à un membre inexistant d'un objet ou d'un élément d'un tableau est définie comme une erreur de structure. Les expressions de chemin SQL/JSON ont deux modes pour la gestion des erreurs de structure :
lax (permissif, par défaut) -- le moteur de chemin adapte implicitement la donnée requêtée au chemin spécifié. Toute erreur de structure qui ne peut être corrigée comme décrit ci-dessous est supprimée, ne produisant aucune correspondance.
strict -- si une erreur de structure survient, une erreur est levée.
Le mode permissif facilite la correspondance d'un document JSON et d'une expression de chemin si la donnée JSON n'est pas conforme au schéma attendu. Si un opérande ne correspond pas aux demandes d'une opération particulière, il peut être automatiquement intégré à un tableau SQL/JSON ou déballé en convertissant ses éléments en une séquence SQL/JSON avant de réaliser cette opération. De plus, les opérateurs de comparaison déballent automatiquement leurs opérandes dans le mode permissif, donc vous pouvez directement comparer les tableaux SQL/JSON. Un tableau de taille 1 est considéré égal à son seul élément. Le déballage automatique n'est pas réalisé quand :
L'expression de chemin contient les méthodes type()
ou size()
qui renvoient respectivement le type et le
nombre d'éléments dans le tableau.
Les données JSON requêtées contiennent des tableaux imbriqués. Dans ce cas, seul le tableau externe est déballé alors que les tableaux internes restent inchangés. De ce fait, le déballage implicite peut seulement descendre d'un niveau à chaque étape d'évaluation du chemin.
Par exemple, lors du requêtage de données GPS ci-dessus, vous pouvez faire abstraction du fait qu'il stocke un tableau de segments en utilisant le mode permissif :
=>
select jsonb_path_query(:'json', 'lax $.track.segments.location');
jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635]
Dans le mode strict, le chemin spécifié doit correspondre exactement à la structure du document JSON requêté, donc utiliser cette expression de chemin causera une erreur :
=>
select jsonb_path_query(:'json', 'strict $.track.segments.location');
ERROR: jsonpath member accessor can only be applied to an object
Pour obtenir le même résultat qu'en mode lax, vous devez déballer explicitement le tableau
segments
:
=>
select jsonb_path_query(:'json', 'strict $.track.segments[*].location');
jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635]
Le comportement de décapsulation
(unwrapping) en mode lax peut entraîner
des résultats surprenants. Par exemple, la requête suivante utilisant
l'opérateur d'accès .**
sélectionne chaque valeur
HR
deux fois :
=>
select jsonb_path_query(:'json', 'lax $.**.HR');
jsonb_path_query ------------------ 73 135 73 135
Ceci survient parce que l'accesseur .**
sélectionne à
la fois le tableau de segments
et chacun de ses
éléments, alors que l'accesseur .HR
déballe
automatiquement les tableaux lors de l'utilisation du mode non strict.
Pour éviter des résultats surprenants, nous recommandons d'utiliser
l'accesseur .**
uniquement dans le mode strict. la
requête suivant sélectionne chaque valeur HR
une seule
fois :
=>
select jsonb_path_query(:'json', 'strict $.**.HR');
jsonb_path_query ------------------ 73 135
La décapsulation (unwrapping) des tableaux
peut également entraîner des résultats inattendus. Considérez cet
exemple, qui sélectionne tous les tableaux location
:
=>
select jsonb_path_query(:'json', 'lax $.track.segments[*].location');
jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635] (2 rows)
Comme prévu, il renvoie les tableaux complets. Mais l'application d'une expression de filtre entraîne la décapsulation (unwrapping) des tableaux pour évaluer chaque élément, ne renvoyant que les éléments qui correspondent à l'expression :
=>
select jsonb_path_query(:'json', 'lax $.track.segments[*].location ?(@[*] > 15)');
jsonb_path_query ------------------ 47.763 47.706 (2 rows)
Ceci malgré le fait que les tableaux complets sont sélectionnés par l'expression de chemin. Utilisez le mode strict pour restaurer la sélection des tableaux :
=>
select jsonb_path_query(:'json', 'strict $.track.segments[*].location ?(@[*] > 15)');
jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635] (2 rows)
Tableau 9.50 montre les opérateurs et
méthodes disponibles pour jsonpath
. Notez que, bien que les
opérateurs et méthodes unaires puissent être appliqués à de nombreuses
valeurs résultant d'une étape de chemin précédente, les opérateurs
binaires (addition etc.) peuvent seulement être appliqués à des valeurs
seules. En mode lax, les méthodes appliquées à un tableau seront
exécutées pour chaque valeur du tableau. Les exceptions sont :
.type()
et .size()
, qui s'appliquent
au tableau lui-même.
Tableau 9.50. Opérateurs et méthodes jsonpath
Opérateur/Méthode Description Exemple(s) |
---|
Addition
|
Plus unaire (pas d'opération) ; contrairement à l'addition, ceci peut itérer sur plusieurs valeurs
|
Soustraction
|
Négation ; contrairement à la soustraction, ceci peut itérer sur plusieurs valeurs
|
Multiplication
|
Division
|
Modulo (reste)
|
Type de l'élément JSON (voir
|
Taille de l'élément JSON (nombre d'éléments d'un tableau, ou 1 si ce n'est pas un tableau)
|
Valeur booléenne convertie à partir d'un booléen, d'un nombre ou d'une chaîne JSON
|
Valeur de type chaîne de caractères convertie en un booléen, nombre, chaîne ou horodatage JSON
|
Nombre flottant approximatif converti en nombre JSON ou en chaîne
|
Entier le plus proche, plus grand ou égal au nombre donné
|
Entier le plus proche, plus petit ou égal au nombre donné
|
Valeur absolue du nombre donné
|
Grande valeur entière convertie à partir d'un nombre JSON ou d'une chaîne JSON
|
Valeur décimale arrondie convertie à partir d'un nombre JSON ou d'une
chaîne JSON (
|
Valeur entière convertie à partir d'un nombre JSON ou d'une chaîne JSON
|
Valeur numérique convertie à partir d'un nombre JSON ou d'une chaîne JSON
|
Valeur date/heure convertie en chaîne
|
Valeur date/heure convertie en une chaîne en utilisant le modèle
|
Valeur date convertie en chaîne
|
Horodatage sans fuseau horaire converti en chaîne
|
Valeur horaire sans fuseau horaire convertie en chaîne de caractères, avec les secondes fractionnelles converties à la précision demandée.
|
Valeur horaire avec fuseau horaire convertie à partir d'une chaîne de caractères.
|
Valeur horaire avec fuseau horaire convertie à partir d'une chaîne de caractères, avec les secondes fractionnelles converties à la précision demandée.
|
Horodatage sans fuseau horaire converti à partir d'une chaîne
|
Horodatage sans fuseau horaire converti à partir d'une chaîne, avec des secondes fractionnaires ajustées à la précision donnée.
|
Horodatage avec fuseau horaire converti à partir d'une chaîne
|
Horodatage avec fuseau horaire converti à partir d'une chaîne, avec des secondes fractionnaires ajustées à la précision donnée.
|
Les paires clé-valeur de l'objet, représentées sous la forme d'un
tableau d'objets contenant trois champs :
|
Le type de résultat des méthodes datetime()
et
datetime(
peut
être template
)date
, timetz
, time
,
timestamptz
ou timestamp
. Les deux méthodes
déterminent dynamiquement le type du résultat.
La méthode datetime()
trie en séquence pour faire
correspondre sa chaîne en entrée aux formats ISO pour les types
date
, timetz
, time
,
timestamptz
et timestamp
. Elle s'arrête au
premier format correspondant et émet le type de données correspondant.
La méthode datetime(
détermine le type de résultat suivant les champs utilisés dans la chaîne
modèle fournie.
template
)
Les méthodes datetime()
et
datetime(
utilisent les mêmes règles d'analyse que la fonction SQL
template
)to_timestamp
(voir Section 9.8), avec trois exceptions. Tout d'abord,
ces méthodes n'autorisent pas les motifs de modèle sans correspondance.
Ensuite, seuls les séparateurs suivants sont autorisés dans la chaîne
modèle : signe moins, point, barre oblique, virgule, apostrophe,
point-virgule, deux-points et espace. Enfin, les séparateurs dans la
chaîne modèle doivent correspondre exactement à la chaîne en entrée.
Si différents types date/heure doivent être comparés, une conversion
implicite est appliquée. Une valeur date
peut être
convertie en timestamp
ou timestamptz
, une
valeur timestamp
peut être convertie en
timestamptz
, et une valeur time
en
timetz
. Néanmoins, toutes sauf la première de ces
conversions dépendent du paramétrage actuel de TimeZone, et de ce fait peuvent seulement être réalisées
à travers les fonctions jsonpath
sensibles au fuseau
horaire. De même, d'autres méthodes liées aux dates/heures qui
convertissent des chaînes en types de date/heure effectuent également
cette conversion, qui peut impliquer le paramètre TimeZone actuel.
Par conséquent, ces conversions ne peuvent être effectuées que si les
fonctions jsonpath
sont conscientes du fuseau horaire.
Tableau 9.51 montre les éléments d'expression de filtre disponibles.
Tableau 9.51. Éléments d'expression de filtre jsonpath
Prédicat/valeur Description Exemple(s) |
---|
Comparaison d'égalité (ceci, et les autres opérateurs de comparaison, fonctionnent sur toutes les valeurs scalaires JSON)
|
Comparaison de non égalité
|
Comparaison inférieur
|
Comparaison inférieur ou égal
|
Comparaison supérieur
|
Comparaison supérieur ou égal
|
Constante JSON
|
Constante JSON
|
Constante JSON
|
AND booléen
|
OR booléen
|
NOT booléen
|
Teste si une condition booléenne est
|
Teste si le premier opérande correspond à l'expression rationnelle
donnée par le deuxième opérande, optionnellement avec les
modifications décrites par une chaîne avec les caractères de
|
Vérifie si le deuxième opérande est une sous-chaîne initiale du premier opérande.
|
Vérifie si une expression de chemin correspond à au moins un élément SQL/JSON.
Renvoie
|
Les expressions de chemin SQL/JSON permettent de faire correspondre du
texte à une expression régulière avec le filtre
like_regex
. Par exemple, la requête de chemin
SQL/JSON suivante ferait correspondre, de manière insensible à la casse,
toutes les chaînes d'un tableau commençant par une voyelle
française :
$[*] ? (@ like_regex "^[aeiouy]" flag "i")
La chaîne de flag
optionnelle peut inclure un ou plusieurs
des caractères suivants :
i
pour une correspondance insensible à la casse,
m
pour permettre à ^
et $
de correspondre aux nouvelles lignes,
s
pour permettre à .
de correspondre
à une nouvelle ligne,
q
pour citer l'ensemble du motif (réduisant le comportement
à une simple correspondance de sous-chaîne).
La norme SQL/JSON emprunte sa définition pour les expressions régulières à
l'opérateur LIKE_REGEX
, qui utilise à son tour la norme
XQuery. PostgreSQL ne prend actuellement pas en charge l'opérateur
LIKE_REGEX
. Par conséquent, le filtre
like_regex
est implémenté en utilisant le moteur d'expressions
régulières POSIX décrit dans Section 9.7.3.
Cela conduit à plusieurs petites divergences par rapport au comportement
standard SQL/JSON, qui sont cataloguées dans Section 9.7.3.8.
Notez toutefois que les incompatibilités de lettres du drapeau
(flag) décrites ici
ne s'appliquent pas à SQL/JSON, car il traduit les lettres du drapeau
XQuery pour correspondre à ce que le moteur POSIX attend.
Gardez à l'esprit que l'argument de motif de like_regex
est une chaîne littérale de chemin JSON, écrite selon les règles données dans
Section 8.14.7. Cela signifie en particulier que tous les
antislashs que vous souhaitez utiliser dans l'expression régulière doivent être
doublés. Par exemple, pour faire correspondre les valeurs de chaîne du document
racine qui ne contiennent que des chiffres :
$.* ? (@ like_regex "^\\d+$")
Les fonctions SQL/JSON JSON_EXISTS()
,
JSON_QUERY()
et JSON_VALUE()
décrites dans Tableau 9.52 peuvent être
utilisées pour interroger des documents JSON. Chacune de ces fonctions
applique une path_expression
(la requête de chemin
SQL/JSON) à un context_item
(le document).
Voir Section 9.16.2 pour plus de détails sur
ce que peut contenir une path_expression
. Le
path_expression
peut aussi référencer des variables,
dont les valeurs sont indiquées avec leur nom respectif dans la clause
PASSING
qui est acceptée par chaque fonction.
context_item
peut être une valeur jsonb
ou une chaîne de caractères convertible en jsonb
.
Tableau 9.52. Fonctions de requête SQL/JSON
Signature de la fonction Description Exemple(s) |
---|
Exemples :
ERROR: jsonpath array subscript is out of bounds
|
Exemples :
ERROR: malformed array literal: "[1, 2]" DETAIL: Missing "]" after array dimensions.
|
Exemples :
|
L'expression context_item
est convertie en
jsonb
par une conversion implicite si l'expression n'est pas
déjà de type jsonb
. Notez, néanmoins, que toute erreur
d'analyse survenant pendant cette conversion, est renvoyée sans condition,
c'est-à-dire ne sont pas gérées suivant la clause ON ERROR
implicite ou explicite.
JSON_VALUE()
renvoie un NULL SQL si
path_expression
renvoie un JSON
null
, alors que JSON_QUERY()
renvoie
le null
JSON tel quel.
JSON_TABLE
est une fonction SQL/JSON qui
interroge les données JSON et présente les
résultats sous forme de vue relationnelle, qui peut être
consultée comme une table SQL normale. Vous pouvez utiliser
JSON_TABLE
dans la clause FROM
d'une instruction SELECT
, UPDATE
ou DELETE
et comme source de données dans une
instruction MERGE
.
En prenant des données JSON en entrée, JSON_TABLE
utilise une expression de chemin JSON pour extraire une partie des
données fournies à utiliser comme motif de ligne
pour la vue construite. Chaque valeur SQL/JSON donnée par le modèle de
ligne sert de source pour une ligne distincte dans la vue construite.
Pour diviser le motif de ligne en colonnes, JSON_TABLE
fournit la clause COLUMNS
qui définit le schéma de la vue
créée. Pour chaque colonne, une expression de chemin JSON distincte peut être
spécifiée pour être évaluée par rapport au motif de ligne afin d'obtenir une
valeur SQL/JSON qui deviendra la valeur de la colonne spécifiée dans une ligne
de sortie donnée.
Les données JSON stockées à un niveau imbriqué du modèle de ligne peuvent être
extraites à l'aide de la clause NESTED PATH
.
Chaque clause NESTED PATH
peut être utilisée pour générer
une ou plusieurs colonnes à partir des données d'un niveau imbriqué du motif de ligne.
Ces colonnes peuvent être spécifiées à l'aide d'une clause COLUMNS
semblable à la clause COLUMNS de niveau le plus haut.
Les lignes construites à partir des colonnes imbriquées sont appelées
lignes enfants et sont jointes aux lignes construites
à partir des colonnes spécifiées dans la clause COLUMNS
parente pour obtenir la ligne dans la vue finale.
Les colonnes enfants elles-mêmes peuvent contenir une spécification
NESTED PATH
, permettant ainsi d'extraire des données
situées à des niveaux d'imbrication arbitraires. Les colonnes produites par plusieurs
NESTED PATH
au même niveau sont considérées comme des
frères et sœurs et leurs lignes sont combinées à l'aide de UNION
après la jonction avec la ligne parente.
Les lignes produites par JSON_TABLE
sont jointes latéralement
à la ligne qui les a générées, de sorte que vous n'avez pas besoin de joindre
explicitement la vue construite avec la table d'origine contenant les données JSON.
La syntaxe est :
JSON_TABLE (context_item
,path_expression
[ ASjson_path_name
] [ PASSING {value
ASvarname
} [, ...] ] COLUMNS (json_table_column
[, ...] ) [ {ERROR
|EMPTY
[ARRAY]}ON ERROR
] ) oùjson_table_column
vaut :name
FOR ORDINALITY |name
type
[ FORMAT JSON [ENCODINGUTF8
]] [ PATHpath_expression
] [ { WITHOUT | WITH { CONDITIONAL | [UNCONDITIONAL] } } [ ARRAY ] WRAPPER ] [ { KEEP | OMIT } QUOTES [ ON SCALAR STRING ] ] [ { ERROR | NULL | EMPTY { [ARRAY] | OBJECT } | DEFAULTexpression
} ON EMPTY ] [ { ERROR | NULL | EMPTY { [ARRAY] | OBJECT } | DEFAULTexpression
} ON ERROR ] |name
type
EXISTS [ PATHpath_expression
] [ { ERROR | TRUE | FALSE | UNKNOWN } ON ERROR ] | NESTED [ PATH ]path_expression
[ ASjson_path_name
] COLUMNS (json_table_column
[, ...] )
Chaque élement de syntaxe est décrit ci-dessous avec plus de détails.
context_item
, path_expression
[ AS
json_path_name
] [ PASSING
{ value
AS
varname
} [, ...]]
context_item
indique le document en entrée à
requêter, path_expression
est une expression de
chemin SQL/JSON définissant la requête, et
json_path_name
est un nom optionnel pour
path_expression
.
La clause optionnelle PASSING
fournit des valeurs aux
données pour les variables mentionnées dans
path_expression
. Le résultat de l'évaluation des
données en entrée en utilisant les éléments déjà mentionnés est appelé
The result of the input data evaluation using the aforementioned elements
row pattern, qui est utilisé comme source pour les
valeurs de ligne dans la vue construite.
COLUMNS
( json_table_column
[, ...] )
La clause COLUMNS
définit le schéma de la vue construite.
Dans cette clause, vous pouvez spécifier chaque colonne à remplir avec une valeur
SQL/JSON obtenue en appliquant une expression de chemin JSON au motif de ligne.
json_table_column
possède les variantes suivantes :
name
FOR ORDINALITY
Ajoute une colonne d'ordinalité qui fournit une numérotation séquentielle
des lignes à partir de 1. Chaque NESTED PATH
(voir ci-dessous) obtient son propre compteur pour les colonnes
d'ordinalité imbriquées.
name
type
[FORMAT JSON
[ENCODING UTF8
]]
[ PATH
path_expression
]
Insère une valeur SQL/JSON obtenue en appliquant
path_expression
au motif de ligne,
dans la ligne de sortie de la vue après l'avoir convertie au
type
spécifié.
La spécification de FORMAT JSON
rend explicite le fait que vous
attendez que la valeur soit un objet json
valide. Il est uniquement
pertinent de spécifier FORMAT JSON
que si
type
est l'un des bpchar
,
bytea
, character varying
, name
,
json
, jsonb
, text
, ou un domaine sur
ces types.
En option, vous pouvez spécifier les clauses WRAPPER
et
QUOTES
pour formater la sortie. Notez que
la spécification de OMIT QUOTES
remplace
FORMAT JSON
si elle est également spécifiée, car les litéraux non cités ne constituent pas des valeurs
json
valides.
En option, vous pouvez utiliser les clauses ON EMPTY
et
ON ERROR
pour spécifier s'il faut remonter l'erreur ou
renvoyer la valeur spécifiée lorsque le résultat de l'évaluation du chemin JSON est
vide, et lorsqu'une erreur se produit pendant l'évaluation du chemin JSON ou lors
de la conversion de la valeur SQL/JSON au type spécifié, respectivement. La
valeur par défaut pour les deux est de renvoyer une valeur NULL
.
Cette clause est convertie en interne et a la même sémantique que
JSON_VALUE
ou JSON_QUERY
.
Ce sera JSON_QUERY
si le type spécifié n'est pas un
type scalaire ou si l'une des clauses FORMAT JSON
,
WRAPPER
ou QUOTES
est présente.
name
type
EXISTS
[ PATH
path_expression
]
Insère une valeur booléenne obtenue en appliquant
path_expression
au motif de ligne,
dans la ligne en sortie de la vue après l'avoir convertie au
type
spécifié.
La valeur correspond à l'application de l'expression PATH
au motif de ligne renvoyant des valeurs ou non.
Le type
spécifié doit avoir une conversion à partir du
type boolean
.
En option, vous pouvez utiliser ON ERROR
pour spécifier s'il faut
remonter l'erreur ou renvoyer la valeur spécifiée lorsqu'une erreur se produit pendant
l'évaluation du chemin JSON ou lors de la conversion de la valeur SQL/JSON au type
spécifié. La valeur par défaut est de renvoyer une valeur booléenne
FALSE
.
Cette clause est convertie en interne et, est sémantiquement équivalent à
JSON_EXISTS
.
NESTED [ PATH ]
path_expression
[ AS
json_path_name
]
COLUMNS
( json_table_column
[, ...] )
Extrait des valeurs SQL/JSON des niveaux imbriqués du modèle de ligne,
génère une ou plusieurs colonnes définies par la sous-clause COLUMNS
,
et insère les valeurs SQL/JSON ayant été extraites dans ces colonnes. L'expression
json_table_column
dans la sous-clause COLUMNS
utilise la même syntaxe que dans la clause COLUMNS
parente.
La syntaxe NESTED PATH
est récursive,
vous pouvez donc utiliser plusieurs niveaux imbriqués en spécifiant plusieurs
sous-clauses NESTED PATH
les unes dans les autres.
Cela permet de décomposer la hiérarchie des objets et tableaux JSON
en un seul appel de la fonction plutôt que de chaîner plusieurs
expressions JSON_TABLE
dans une instruction SQL.
Dans chaque variante de json_table_column
décrite
ci-dessus, si la clause PATH
est omise, l'expression de chemin
$.
est utilisée, où
name
name
est le nom de colonne fourni.
AS
json_path_name
Le json_path_name
optionnel sert d'identifiant du
path_expression
fourni. Le nom doit être unique et distinct
des noms de colonnes.
ERROR
| EMPTY
} ON ERROR
La clause optionnelle ON ERROR
peut être utilisée pour spécifier comment
gérer les erreurs lors de l'évaluation du path_expression
de niveau supérieur.
Utilisez ERROR
si vous souhaitez que les erreurs se génèrent et EMPTY
pour
renvoyer une table vide, c'est-à-dire une table contenant 0 ligne. Notez que
cette clause n'affecte pas les erreurs qui surviennent lors de l'évaluation
des colonnes, pour lesquelles le comportement dépend de la spécification de la clause
ON ERROR
pour une colonne donnée.
Examples
Dans les exemples suivants, la table ci-dessous sera utilisée avec les données JSON indiquées :
CREATE TABLE my_films ( js jsonb ); INSERT INTO my_films VALUES ( '{ "favorites" : [ { "kind" : "comedy", "films" : [ { "title" : "Bananas", "director" : "Woody Allen"}, { "title" : "The Dinner Game", "director" : "Francis Veber" } ] }, { "kind" : "horror", "films" : [ { "title" : "Psycho", "director" : "Alfred Hitchcock" } ] }, { "kind" : "thriller", "films" : [ { "title" : "Vertigo", "director" : "Alfred Hitchcock" } ] }, { "kind" : "drama", "films" : [ { "title" : "Yojimbo", "director" : "Akira Kurosawa" } ] } ] }');
La requête suivante montre comment utiliser JSON_TABLE
pour
transformer les objets JSON de la table my_films
en une vue contenant des colonnes pour les clés kind
,
title
et director
contenues dans
le JSON original ainsi qu'une colonne d'ordinalité :
SELECT jt.* FROM my_films, JSON_TABLE (js, '$.favorites[*]' COLUMNS ( id FOR ORDINALITY, kind text PATH '$.kind', title text PATH '$.films[*].title' WITH WRAPPER, director text PATH '$.films[*].director' WITH WRAPPER)) AS jt;
id | kind | title | director ----+----------+--------------------------------+---------------------------------- 1 | comedy | ["Bananas", "The Dinner Game"] | ["Woody Allen", "Francis Veber"] 2 | horror | ["Psycho"] | ["Alfred Hitchcock"] 3 | thriller | ["Vertigo"] | ["Alfred Hitchcock"] 4 | drama | ["Yojimbo"] | ["Akira Kurosawa"] (4 rows)
Voici une version modifiée de la requête ci-dessus pour montrer
l'utilisation des arguments PASSING
dans le filtre
spécifié dans l'expression de chemin JSON de niveau supérieur ainsi que
les différentes options pour les colonnes individuelles :
SELECT jt.* FROM my_films, JSON_TABLE (js, '$.favorites[*] ? (@.films[*].director == $filter)' PASSING 'Alfred Hitchcock' AS filter, 'Vertigo' AS filter2 COLUMNS ( id FOR ORDINALITY, kind text PATH '$.kind', title text FORMAT JSON PATH '$.films[*].title' OMIT QUOTES, director text PATH '$.films[*].director' KEEP QUOTES)) AS jt;
id | kind | title | director ----+----------+---------+-------------------- 1 | horror | Psycho | "Alfred Hitchcock" 2 | thriller | Vertigo | "Alfred Hitchcock" (2 rows)
Voici une version modifiée de la requête ci-dessus pour montrer l'utilisation
de NESTED PATH
pour remplir les colonnes title et director,
illustrant comment elles sont jointes aux colonnes parents id
et kind :
SELECT jt.* FROM my_films, JSON_TABLE ( js, '$.favorites[*] ? (@.films[*].director == $filter)' PASSING 'Alfred Hitchcock' AS filter COLUMNS ( id FOR ORDINALITY, kind text PATH '$.kind', NESTED PATH '$.films[*]' COLUMNS ( title text FORMAT JSON PATH '$.title' OMIT QUOTES, director text PATH '$.director' KEEP QUOTES))) AS jt;
id | kind | title | director ----+----------+---------+-------------------- 1 | horror | Psycho | "Alfred Hitchcock" 2 | thriller | Vertigo | "Alfred Hitchcock" (2 rows)
La prochaine est la même requête, mais cette fois ci, sans le filtre dans le chemin racine :
SELECT jt.* FROM my_films, JSON_TABLE ( js, '$.favorites[*]' COLUMNS ( id FOR ORDINALITY, kind text PATH '$.kind', NESTED PATH '$.films[*]' COLUMNS ( title text FORMAT JSON PATH '$.title' OMIT QUOTES, director text PATH '$.director' KEEP QUOTES))) AS jt;
id | kind | title | director ----+----------+-----------------+-------------------- 1 | comedy | Bananas | "Woody Allen" 1 | comedy | The Dinner Game | "Francis Veber" 2 | horror | Psycho | "Alfred Hitchcock" 3 | thriller | Vertigo | "Alfred Hitchcock" 4 | drama | Yojimbo | "Akira Kurosawa" (5 rows)
Voici une autre requête utilisant un objet JSON
différent comme entrée.
Elle montre l'union "sibling join" (frères et sœurs) entre les chemins NESTED
$.movies[*]
et $.books[*]
et également
l'utilisation de la colonne FOR ORDINALITY
aux niveaux
NESTED
(colonnes movie_id
,
book_id
et author_id
) :
SELECT * FROM JSON_TABLE ( '{"favorites": {"movies": [{"name": "One", "director": "John Doe"}, {"name": "Two", "director": "Don Joe"}], "books": [{"name": "Mystery", "authors": [{"name": "Brown Dan"}]}, {"name": "Wonder", "authors": [{"name": "Jun Murakami"}, {"name":"Craig Doe"}]}] }}'::json, '$.favorites[*]' COLUMNS ( user_id FOR ORDINALITY, NESTED '$.movies[*]' COLUMNS ( movie_id FOR ORDINALITY, mname text PATH '$.name', director text), NESTED '$.books[*]' COLUMNS ( book_id FOR ORDINALITY, bname text PATH '$.name', NESTED '$.authors[*]' COLUMNS ( author_id FOR ORDINALITY, author_name text PATH '$.name'))));
user_id | movie_id | mname | director | book_id | bname | author_id | author_name ---------+----------+-------+----------+---------+---------+-----------+-------------- 1 | 1 | One | John Doe | | | | 1 | 2 | Two | Don Joe | | | | 1 | | | | 1 | Mystery | 1 | Brown Dan 1 | | | | 2 | Wonder | 1 | Jun Murakami 1 | | | | 2 | Wonder | 2 | Craig Doe (5 rows)