Overview

A jsonpath expression such as $.items[*] ? (@.price > 100) navigates and filters inside a jsonb value. Switch from the arrow operators (->, ->>, #>) to jsonpath once a query must filter inside arrays or test conditions on nested objects. Use containment (@>) for plain key-value matches; it is simpler and GIN-indexable.

Rules

  • Functions: jsonb_path_exists, jsonb_path_query, jsonb_path_query_array, jsonb_path_match. The operators @? (path matches anything) and @@ (predicate result) are the same checks in operator form.
  • Index with the operator, not the function. A GIN index serves @? and @@, but jsonb_path_exists(...) in WHERE cannot use it.
  • GIN extracts only clauses of the form accessor_chain == constant. Inequality filters such as > 100 are not index-accelerated.
  • jsonb_path_ops is smaller and faster but drops the key-exists operators and .* accessors.
  • PostgreSQL 17 added the SQL standard JSON_EXISTS, JSON_QUERY, JSON_VALUE, and JSON_TABLE. Normalize data that you query relationally into columns.
SELECT id FROM orders WHERE data @? '$.items[*] ? (@.sku == "A1")';
CREATE INDEX idx_orders_data ON orders USING gin (data jsonb_path_ops);