Module P-5·18 min read

JSONB vs JSON, containment operators, GIN indexes, and when JSONB is the right tool vs. a schema design shortcut.

P-5 — JSON and JSONB: Working With Semi-Structured Data

Who this module is for: You have normalised relational schemas down. Sometimes, though, data does not fit neatly into fixed columns — product attributes vary by category, user preferences are open-ended, API responses have unpredictable shapes. PostgreSQL's JSONB gives you a relational database that also handles document-style data. This module covers when to reach for it, how to query it efficiently, and when using it is a mistake that will cost you later.


JSON vs JSONB — Choose JSONB

PostgreSQL has two JSON types:

JSON — stores the raw text of the JSON document, preserving whitespace, duplicate keys, and key ordering. Validation only on insert; no indexing support. Slower to process because it re-parses on every operation.

JSONB — stores JSON in a decomposed binary format. Duplicate keys are removed (last value wins), key ordering is not preserved, but: supports indexing, supports all operators, and processes faster. Use JSONB for everything.

sql

Inserting JSONB

sql

Querying JSONB — The Operator Reference

Extracting values

sql

Filtering with JSONB operators

sql

Modifying JSONB

sql

Building JSON from Relational Data

sql

GIN Indexes — Making JSONB Queries Fast

Without an index, every JSONB query scans every row. With a GIN index, containment (@>) and key existence (?, ?|, ?&) queries are fast.

sql

GIN index with specific path (index only one field — more efficient):

sql

JSONB Query Path Language (jsonpath)

PostgreSQL 12+ includes jsonpath — a mini-language for complex JSONB queries:

sql

When JSONB Is the Right Tool

Use JSONB when:

  1. The schema is genuinely variable — product attributes differ by category (electronics have voltage, clothing has size/material), and you cannot enumerate all attributes upfront

  2. Semi-structured external data — storing API responses, webhook payloads, or log data where the shape is not fully under your control

  3. Infrequently queried sub-attributes — metadata that you store for display but rarely filter on

  4. Rapid prototyping — the schema is not yet stable; JSONB buys time to define it

sql

When JSONB Is the Wrong Tool

Do NOT use JSONB when:

  1. The data is structured and you query individual fields — if you always WHERE attributes->>'status' = 'active', that should be a proper TEXT NOT NULL column with a B-tree index

  2. You need referential integrity — JSONB values cannot be foreign keys; you cannot REFERENCES a value inside a JSONB document

  3. You need constraints on sub-fields — you cannot add CHECK (attributes->>'price' > 0) in a meaningful way; use a real column

  4. The data is always the same shape — if every row has {"first_name": "...", "last_name": "..."}, that is two columns, not a JSONB object

sql

Practical Exercise: Product Catalog with Variable Attributes

sql

Summary

ConceptKey Takeaway
JSON vs JSONBAlways use JSONB — indexable, faster processing
->Extract as JSONB (preserves type for nested access)
->>Extract as TEXT (use for comparisons)
@>Containment check — main operator for JSONB filtering
? / `?/?&`
`
jsonb_set()Update a nested value without replacing entire document
GIN indexRequired for fast @> and ? queries on large tables
jsonb_build_object()Construct JSONB in queries
jsonb_agg()Aggregate rows into a JSON array
Good use casesVariable attributes, external API data, open-ended metadata
Bad use casesStructured data you always query, data needing FK constraints or CHECK constraints

Module P-6 covers access control — how to set up roles with the right permissions, and how Row-Level Security policies make data isolation automatic at the database level.

Next: P-6 — Authentication, Row-Level Security, and Access Control →

Knowledge Check

A development team decides to store their entire users table as a single JSONB column to "avoid schema migrations in the future". As a Senior Principal Software Engineer, why is this an architectural anti-pattern for a core relational entity like a user?


You are querying a products table with a JSONB column named attributes. You need to find all products where the battery_hours attribute is strictly greater than 20. Which of the following queries is the syntactically correct and optimal way to achieve this?


A highly trafficked e-commerce application frequently runs this exact query to find products in a specific color: SELECT * FROM products WHERE attributes @> '{"color": "black"}';. A developer wants to speed this up and runs: CREATE INDEX ON products ((attributes ->> 'color'));. After deployment, query performance does not improve. Why?

Test your knowledge with more question sets

Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.