JSONB column lets you store flexible nesting; @> containment operator lets you query “this JSON has these keys/values”. With a GIN index the query is index-speed. JSONB gives AI apps a typed-key store without a separate metadata table.
metadata jsonb; WHERE metadata @> ‘{”source_kind”: “pdf”}’::jsonb — fast with GIN index on metadata.
GIN jsonb_path_ops index is smaller/faster than default and supports only @> (no ?/?&).
jsonb_set(metadata, ‘{tags}’, ’[“rag”,“langgraph”]‘::jsonb, true) merges without losing other keys.
doc -> jsonb metadata -> @> query -> GIN index
index narrows fast; jsonset merges
JSONB + GIN = flexible metadata without a separate model. Use @> for most queries.