Topic 4.5
JSON and the Query Engine: Documents with Indexes
In one line
Redis 8 stores native JSON documents you can read and update by path, and the Query Engine adds secondary indexes over hashes and JSON: full-text, numeric ranges, tags, geo, sorting and aggregations. It turns Redis from key-only lookup into a queryable, in-memory document store.
Think of it like this
A library card catalogue. Books sit on shelves by ID (keys), but the catalogue lets you find them by author, subject or year without walking every shelf. The Query Engine is that catalogue, kept up to date every time a book is added or changed.
Key ideas
- 01
JSON commands:
JSON.SET key $ '{...}',JSON.GET key $.name $.price,JSON.MGET,JSON.NUMINCRBY key $.stock -1,JSON.ARRAPPEND,JSON.DEL key $.path,JSON.TYPE. Paths use JSONPath ($); updates change only the targeted part, atomically, without rewriting the whole document from the client. - 02
Indexes:
FT.CREATE idx ON JSON|HASH PREFIX 1 product: SCHEMA ...declares fields asTEXT(full-text, stemming),TAG(exact values, like brand or status),NUMERIC(ranges,SORTABLE),GEO, andVECTOR(Topic 4.6). Every write to a key matching the prefix updates the index synchronously, so queries see your write immediately. - 03
Queries:
FT.SEARCH idx "@brand:{acme} @price:[100 500] laptop" SORTBY price ASC LIMIT 0 10 RETURN 2 $.name $.price. Aggregations withFT.AGGREGATE ... GROUPBY 1 @brand REDUCE COUNT 0 AS n. Autocomplete withFT.SUGADDandFT.SUGGET. - 04
Costs: indexes use extra memory (often 20–100% of the indexed data), writes do more work, and big result sets are expensive. Keep indexes to fields you actually query; use
TAGrather thanTEXTfor exact matches. - 05
Availability: built into Redis 8 Open Source; previously Redis Stack. On Valkey, JSON and search come from separate modules (valkey-json, valkey-search), and support in managed services varies, so check your provider. Clustered deployments need a query engine that coordinates across shards, so confirm your edition supports it before relying on it.
- 06
When to use it: fast product filtering, session or entity lookup by attribute, autocomplete, real-time dashboards over hot data. When not: your source of truth needs joins, transactions across entities, or long-term storage, where PostgreSQL (or Elasticsearch/OpenSearch for large search workloads) fits better.
Code & diagrams
127.0.0.1:6379> JSON.SET product:1 $ '{"name":"Laptop Pro 14","brand":"acme","price":1299,"stock":12,"tags":["laptop","14in"]}'
OK
127.0.0.1:6379> JSON.SET product:2 $ '{"name":"Laptop Air 13","brand":"acme","price":899,"stock":0,"tags":["laptop"]}'
OK
127.0.0.1:6379> JSON.NUMINCRBY product:1 $.stock -1
"[11]"
127.0.0.1:6379> JSON.GET product:1 $.name $.stock
"{\"$.name\":[\"Laptop Pro 14\"],\"$.stock\":[11]}"
127.0.0.1:6379> FT.CREATE idx:product ON JSON PREFIX 1 product: SCHEMA $.name AS name TEXT $.brand AS brand TAG $.price AS price NUMERIC SORTABLE $.stock AS stock NUMERIC
OK
127.0.0.1:6379> FT.SEARCH idx:product "@brand:{acme} @price:[800 1500] @stock:[1 +inf]" SORTBY price ASC RETURN 2 name price
1) (integer) 1
2) "product:1"
3) 1) "name"
2) "Laptop Pro 14"
3) "price"
4) "1299"
127.0.0.1:6379> FT.AGGREGATE idx:product "*" GROUPBY 1 @brand REDUCE COUNT 0 AS products
1) (integer) 1
2) 1) "brand" 2) "acme" 3) "products" 4) "2"Interview problem
The problem
Fast product filtering
The product listing page filters by brand, price range and in-stock, sorted by price, with 200K products and 5K queries/sec. PostgreSQL struggles with the combination of filters and sort. Would you use the Redis Query Engine? How do you keep it in sync?
You're given
- 200K products
- 5K filter queries/sec
- Price and stock change often
- PostgreSQL is the source of truth
The interviewer follows up
Why not Elasticsearch?
Explain it without notes
How does the Query Engine keep indexes up to date, and what does that mean for write performance?
Practice
Index hashes of users (user:{id} with name, country, age) and query users from India aged 25–35 sorted by age.
Trade-offs
- ↔
Query Engine: millisecond queries on hot data with one fewer system, but more memory, slower writes, and a sync pipeline from the source of truth.
- ↔
JSON documents give nesting and path updates; hashes are more compact for flat objects.
Done when you can
I can store and update JSON by path.
I can create an index and write filter, sort and aggregate queries.
I can design sync from a database with versioning and rebuilds.