intermediate
JSONB
Store flexible attributes in JSONB while keeping queryable invariants, indexes, and schema ownership clear.
JSONB stores binary JSON with efficient containment queries. Use it for semi-structured attributes that change shape, not as a substitute for relational invariants.
-- Containment (@>) uses GIN well
CREATE INDEX idx_meta_gin ON products USING GIN (metadata jsonb_path_ops);
SELECT id, metadata->>'color' AS color
FROM products
WHERE metadata @> '{"category": "shoes"}';
-- jsonb_set for partial updates
UPDATE products
SET metadata = jsonb_set(metadata, '{tags}', '["sale"]'::jsonb, true)
WHERE id = 42;
Keep queryable fields explicit: promote stable keys to typed columns or generated columns when they drive joins, constraints, or reporting. JSONB shines for optional attributes, event payloads, and config blobs — not for every field in a core entity.
On interviews: explain when JSONB beats EAV tables, how GIN indexes help @> and ? operators, and why schema discipline still matters (migrations, validation, ownership).
Common pitfalls: no indexes on hot JSON paths; storing money or dates only as strings; unbounded document growth; hiding foreign keys inside JSON without integrity checks.
The trade-off is schema flexibility versus query predictability — JSONB defers structure cost to application validation and index design.
Checklist:
- Index paths that appear in WHERE/ORDER BY.
- Promote stable keys to columns when they are join keys.
- Validate shape at write boundaries.
- Measure update size and TOAST overhead on large documents.