JSONB : le schéma flexible sans quitter le relationnel
Le besoin : des attributs qu'on ne connaĂźt pas Ă l'avance
Un catalogue produit multi-tenant : chaque client a SES attributs (matiĂšre, voltage, DLCâŠ). Une colonne par attribut est impossible. La rĂ©ponse Postgres : une colonne JSONB â du JSON binaire, indexable et interrogeable. C'est exactement le pattern extensions du projet Ă©tudiĂ© (le nom du variant, les specs libres y vivent), et celui de cette plateforme (gardens.state).
-- interroger
SELECT * FROM variant WHERE extensions->>'marque' = 'Nike'; -- ->> = texte
SELECT * FROM variant WHERE (extensions->>'voltage')::int > 220; -- cast si besoin
SELECT * FROM variant WHERE extensions @> '{"actif": true}'; -- @> = contient
SELECT * FROM variant WHERE extensions ? 'garantie'; -- ? = clé existe
Indexer le JSONB (sinon Seq Scan garanti)
Un filtre sur JSONB sans index scanne toute la table. Deux stratégies :
- index GIN sur la colonne :
CREATE INDEX ⊠USING gin (extensions)â accĂ©lĂšre@>,?,?&(les opĂ©rateurs de contenance/existence). Le couteau suisse ; - index B-tree sur une expression :
CREATE INDEX ⊠ON variant ((extensions->>'marque'))â pour un attribut prĂ©cis frĂ©quemment filtrĂ© par Ă©galitĂ©/ordre. Plus petit, plus rapide sur CE champ.
Choix : requĂȘtes variĂ©es sur le contenu â GIN ; un champ chaud identifiĂ© â index d'expression B-tree.
Quand JSONB, quand colonne (la vraie décision)
JSONB est un outil, pas un défaut. La ligne de partage :
- colonne SQL pour ce qui est : su Ă l'avance, filtrĂ©/joint souvent, contraint (types, FK, NOT NULL), partagĂ© par tous les tenants. Le prix, le stock, le tenant_id â jamais en JSONB ;
- JSONB pour ce qui est : variable par tenant, rarement filtré (souvent juste lu/affiché), sans contrainte forte. Les attributs libres, les métadonnées, un état de jeu.
Le piĂšge classique : tout mettre en JSONB « pour la flexibilitĂ© » â on perd les contraintes, les FK, la lisibilitĂ© des requĂȘtes, et on rĂ©invente un schĂ©ma sans filet. Le projet Ă©tudiĂ© tranche par ADR : variant.name en JSONB (varie par tenant), price.level dĂ©normalisĂ© en colonne (filtrĂ© en permanence). Chaque champ, une dĂ©cision.
L'opĂ©rateur @> sur JSONB testeâŠ