Fonctions JSON dans le Query Builder
Fonctions JSON dans le Query Builder
Dodock fournit trois fonctions pour lire des champs JSON dans frappe.qb : JSONValue, JSONExtract et JSONContains. Elles extraient ou filtrent des valeurs imbriquées dans la requête SQL. Vous n'avez donc pas à charger les documents en mémoire.
$, par exemple $.codes. Sur PostgreSQL, Dodock utilise les opérateurs ->, ->> et @> : le chemin est alors le nom d'une clé, par exemple codes.Les exemples utilisent des champs JSON personnalisés (preferences_json, applied_discounts_json) et un type de document fictif Profile. Ces champs n'existent pas dans Dokos : adaptez les noms à vos propres champs JSON.
Import
from frappe.query_builder.functions import JSONValue, JSONExtract, JSONContains
JSONValue
JSONValue(champ, chemin) extrait une valeur scalaire (chaîne, nombre, booléen) depuis un champ JSON en suivant un chemin JSONPath.
Syntaxe
JSONValue(champ, chemin_jsonpath)
| Paramètre | Type | Description |
|---|---|---|
champ | Colonne Dodock | Champ du DocType contenant la valeur JSON |
chemin_jsonpath | str | Expression JSONPath commençant par $ |
Exemple : filtrer sur une valeur imbriquée
Soit un DocType Profile avec un champ preferences_json stockant :
{"notifications": {"email": true, "sms": false}, "language": "en"}
import frappe
from frappe.query_builder.functions import JSONValue
Profile = frappe.qb.DocType("Profile")
resultats = (
frappe.qb.from_(Profile)
.select(
Profile.customer_name,
JSONValue(Profile.preferences_json, "$.notifications.email").as_("notif_email"),
JSONValue(Profile.preferences_json, "$.language").as_("langue"),
)
.where(JSONValue(Profile.preferences_json, "$.notifications.email") == "true")
).run(as_dict=True)
Cette requête retourne uniquement les profils dont les notifications par e-mail sont activées, en exposant directement les valeurs extraites comme colonnes nommées.
JSONExtract
JSONExtract(champ, chemin) extrait une valeur ou un fragment JSON (y compris un tableau ou un objet imbriqué) depuis un champ JSON. Contrairement à JSONValue, le résultat peut être un tableau JSON ou un objet, pas seulement une valeur scalaire.
Syntaxe
JSONExtract(champ, chemin_jsonpath)
Exemple : extraire un tableau imbriqué
Soit un champ personnalisé applied_discounts_json sur Commande client (Sales Order), qui contient :
{"codes": ["WELCOME10", "FREESHIP", "VIP"]}
from frappe.query_builder.functions import JSONExtract
SalesOrder = frappe.qb.DocType("Sales Order")
codes = (
frappe.qb.from_(SalesOrder)
.select(
SalesOrder.name,
JSONExtract(SalesOrder.applied_discounts_json, "$.codes").as_("codes_remise"),
)
).run(as_dict=True)
Le résultat de JSONExtract sur un tableau est une chaîne JSON de type ["WELCOME10", "FREESHIP", "VIP"], que vous pouvez ensuite passer à JSONContains.
JSONContains
JSONContains(cible, valeur) vérifie si une valeur est contenue dans un document ou un tableau JSON. Elle est particulièrement utile pour filtrer des listes stockées en JSON.
Syntaxe
JSONContains(cible, valeur)
| Paramètre | Type | Description |
|---|---|---|
cible | Colonne ou résultat de JSONExtract | Fragment JSON dans lequel chercher |
valeur | str | Valeur JSON à rechercher (encodée en chaîne) |
Exemple : filtrer les commandes contenant un code promo
import frappe
from frappe.query_builder.functions import JSONContains, JSONExtract
SalesOrder = frappe.qb.DocType("Sales Order")
commandes = (
frappe.qb.from_(SalesOrder)
.select(SalesOrder.name, SalesOrder.customer)
.where(
JSONContains(
JSONExtract(SalesOrder.applied_discounts_json, "$.codes"),
'"FREESHIP"',
)
)
).run(as_dict=True)
'"FREESHIP"' et non 'FREESHIP'.Combiner les fonctions
Les trois fonctions se combinent. Cet exemple combine JSONExtract et JSONContains pour filtrer les commandes client ayant bénéficié de la livraison gratuite :
import frappe
from frappe.query_builder.functions import JSONContains, JSONExtract, JSONValue
SalesOrder = frappe.qb.DocType("Sales Order")
Profile = frappe.qb.DocType("Profile")
# Commandes avec livraison gratuite pour les clients ayant activé les notifications e-mail
resultats = (
frappe.qb.from_(SalesOrder)
.inner_join(Profile)
.on(SalesOrder.customer == Profile.name)
.select(SalesOrder.name, SalesOrder.customer, SalesOrder.grand_total)
.where(
JSONContains(
JSONExtract(SalesOrder.applied_discounts_json, "$.codes"),
'"FREESHIP"',
)
)
.where(JSONValue(Profile.preferences_json, "$.notifications.email") == "true")
).run(as_dict=True)
Référence rapide
| Fonction | Équivalent SQL | Retourne | Cas d'usage typique |
|---|---|---|---|
JSONValue(champ, chemin) | JSON_UNQUOTE(JSON_EXTRACT()) (MariaDB), ->> (PostgreSQL) | Valeur scalaire sans guillemets | Lire $.language, $.price |
JSONExtract(champ, chemin) | JSON_EXTRACT() (MariaDB), -> (PostgreSQL) | Valeur ou fragment JSON | Extraire un tableau $.codes |
JSONContains(cible, valeur) | JSON_CONTAINS() (MariaDB), @> (PostgreSQL) | 0 ou 1 | Vérifier la présence dans un tableau |