JSON Aggregation and Querying
Aggregate related rows into JSON arrays and query JSONB columns with PostgreSQL native JSON operators.
-- Aggregate orders with their items as JSON array
SELECT
o.id AS order_id,
o.created_at,
o.total,
json_agg(
json_build_object(
'product', p.name,
'quantity', oi.quantity,
'price', oi.price
) ORDER BY p.name
) AS items
FROM orders o
JOIN order_items oi ON oi.order_id = o.id
JOIN products p ON p.id = oi.product_id
GROUP BY o.id;
-- Query JSONB column
SELECT id, data->>'name' AS name
FROM events
WHERE data @> '{"type": "purchase"}' -- contains
AND (data->>'amount')::int > 100;
-- Build nested JSON response in one query
SELECT json_build_object(
'user', json_build_object('id', u.id, 'name', u.name),
'posts', (
SELECT json_agg(json_build_object('id', p.id, 'title', p.title))
FROM posts p WHERE p.user_id = u.id
),
'stats', json_build_object(
'post_count', (SELECT count(*) FROM posts WHERE user_id = u.id),
'joined', u.created_at
)
) AS profile
FROM users u
WHERE u.id = 1;Use Cases
- API response building
- Denormalized data queries
- No-ORM data access
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
Keyset Pagination vs Offset
Efficient keyset (cursor) pagination pattern compared to traditional OFFSET for large datasets in PostgreSQL.
UPSERT with ON CONFLICT
Insert or update rows atomically using PostgreSQL ON CONFLICT clause with partial indexes and conditions.
Full-Text Search with Ranking
PostgreSQL full-text search using tsvector, tsquery, and ts_rank with trigram similarity for fuzzy matching.
Window Functions with RANK and LAG
Use window functions to rank rows, calculate running totals, and compare with previous rows without self-joins.