Section 7: Lena, an Explosion and a Car Chase at Night - Deep Dive: Search — Temporal, Spatial and Semantic Queries
Executive Summary: Every search starts the same way. The compiler resolves the scope pointers for the entities in play (one Cassandra partition read, cached 5 s), then sends small, op-filtered queries to the right per-schema Elasticsearch indices. Joins happen in the query service, not in Elasticsearch. Priya's "Lena on screen during an explosion" becomes two queries of about 60 and 40 hits plus a sweep-line merge that finishes in well under a millisecond, inside a 300 ms p99 budget. Spatial filters run on normalized coordinates. "Car chases at night" is a filtered kNN over 67.5M int8-quantized shot embeddings on 3 dedicated vector nodes, inside 800 ms p99. Catalog-wide results use is_current plus a pointer post-filter. Deep pages use search_after with a point-in-time, never from/size.
The interviewer caps the marker. "You've got two deep dives. Where do you want to go?"
You: "Search first, then operations. Search is where the three query shapes split: time, space and meaning each need a different mechanism. Operations are the hardest correctness problem, so I'd like to save them for a clean look in Section 8. Search depends on one thing from there, the pointer, and I'll treat it as a black box for now."
One DSL, One Compiler
Section 4 gave consumers one POST /v1/search DSL. The Query API fleet from Section 6 compiles every request through the same pipeline:
For one title, pointer resolution is a single read of the scope_pointers partition for title:70451122. That one row set names the current op for every (schema, producer) on Night Heist: op_7Hq2 for char-detect, plus the current ops for object-detect v7 and for the human producer. The query service caches it for 5 s and drops it early when an operation-events message says the scope flipped.
Each per-type query then filters on those op ids. Here is the Lena half of Priya's first question:
{ "query": { "bool": { "filter": [
{ "term": { "entity_id": "title:70451122" } },
{ "term": { "op_id": "op_7Hq2" } },
{ "term": { "data.character": "Lena Cruz" } } ] } },
"_source": ["temporal"] }
Filter-only clauses skip scoring, and the short _source list keeps the response tiny. The join needs only ids and time ranges, and every hit already carries its _id, which is the annotation id. The human layer is a separate query, merged per merge_policy (Section 11).
Interviewer: "You have an is_current flag on every doc. Why not filter on that for one title too?"
You: "Because the flag lags. After a flip, the reconciler takes up to 60 seconds to re-index it. The pointer is exact, and for one title it costs one cached partition read. The flag only earns its keep when I can't enumerate the entities, which is the catalog-wide case."
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson