What is JSON and JSONB in PostgreSQL?
JSON (JavaScript Object Notation) is a lightweight format for representing structured data as key-value objects and ordered arrays. PostgreSQL supports two types for storing JSON:
- JSON — stores an exact copy of the input text, preserving whitespace, key order, and duplicate keys.
- JSONB — parses and stores JSON in binary format. Smaller on disk, faster to query, supports GIN indexes. Does not preserve whitespace, key order, or duplicate keys.
In practice, use JSONB for almost all use cases. Reserve JSON only when you must preserve the exact original formatting.
Storing JSON objects
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
properties JSONB
);
INSERT INTO products (name, properties) VALUES
('Ink Fusion T-Shirt', '{"color": "white", "size": ["S","M","L","XL"]}'),
('ThreadVerse T-Shirt', '{"color": "black", "size": ["S","M","L","XL"]}'),
('Design Dynamo T-Shirt', '{"color": "blue", "size": ["S","M","L","XL"]}')
RETURNING *;
Extracting fields with -> and ->>
-- -> returns a JSON value (with quotes for strings)
SELECT id, name, properties -> 'color' AS color
FROM products;
-- "white", "black", "blue"
-- ->> returns a text value (no quotes)
SELECT id, name, properties ->> 'color' AS color
FROM products;
-- white, black, blue
Filtering with JSONB in WHERE
-- Exact match using ->>
SELECT id, name, properties ->> 'color' AS color
FROM products
WHERE properties ->> 'color' IN ('black', 'white');
-- Containment query using @>
SELECT id, name
FROM products
WHERE properties @> '{"color": "blue"}';
Storing and querying JSON arrays
CREATE TABLE contacts (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
phones JSONB
);
INSERT INTO contacts (name, phones) VALUES
('John Doe', '["408-111-2222", "408-111-2223"]'),
('Jane Doe', '["212-111-2222", "212-111-2223"]');
-- Get the first phone number (index 0)
SELECT name, phones ->> 0 AS work_phone
FROM contacts;
-- Output:
-- name | work_phone
-- ---------+--------------
-- John Doe | 408-111-2222
-- Jane Doe | 212-111-2222
Indexing JSONB for performance
-- GIN index for containment and key-existence queries
CREATE INDEX idx_products_properties ON products USING GIN (properties);
-- Expression index for queries on a specific key
CREATE INDEX idx_products_color ON products ((properties ->> 'color'));
Production tips
- Always choose
JSONBoverJSONunless you have a specific need to preserve exact input formatting. - Create a GIN index on JSONB columns that are frequently searched with
@>,?,?&, or?|operators. - For frequently queried specific fields, use expression indexes on extracted values (
(data ->> 'key')) — they are more selective than full GIN indexes. - Avoid storing very large JSON documents (megabytes) in JSONB columns — large values trigger PostgreSQL's TOAST compression/storage, which adds overhead.
Reference: PostgreSQL documentation — JSON types.