</> MAANG.io
system design · 301

Advanced

Master system design interviews with scalable architecture patterns, distributed systems, and real-world design challenges.

0/480 solved 0% complete

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:

Search compiler pipeline: POST /v1/search is parsed and validated, gets the caller's clearance filter, then resolves scope pointers if entity-scoped or filters on is_current if catalog-wide, runs per-type ES queries by op_id, combines, post-filters and hydrates, and pages with an opaque cursor

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

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.