Skip to content

CONCEPT Cited by 1 source

Nested document indexing

Definition

Nested document indexing is the Elasticsearch document structure where a single top-level document carries a field typed as nested whose values are an array of independently- indexed child documents. Each child document's fields are queried independently — a query expressing "child with field A = x AND field B = y" matches only children where the same child has both fields, not two separate children each with one field.

Canonical wiki instance: Netflix's multimodal video-search index stores each temporal bucket as a nested document: "The root level captures the overarching asset context, while associated child documents house the specific, multi-modal annotation data. This hierarchical data model is precisely what empowers users to execute highly efficient, cross-annotation queries at scale" (Source: sources/2026-04-04-netflix-powering-multimodal-intelligence-for-video-search).

Why nested over flat

A flat document model (one row per annotation, asset + bucket denormalized on every row) works for simple queries but falls over on cross-annotation constraints within a single bucket:

  • "find buckets where a character annotation has confidence > 0.8 AND a scene annotation has label='kitchen'" over a flat table can match across annotation rows in separate buckets.
  • Nested documents keep each annotation as a self-contained unit while grouping them under the parent bucket's (asset_id, time_bucket) identity — so cross-field constraints apply within a single nested child.

The alternative — parent-child (separate indices) — trades query-performance for write-flexibility; nested is write-atomic and read-efficient but requires full-document reindexing on any change.

Schema shape (Netflix)

root document
├── associated_ids
│   ├── MOVIE_ID
│   └── ASSET_ID
├── time_bucket_start_ns
├── time_bucket_end_ns
└── source_annotations: [nested]
    ├── { annotation_id, annotation_type, label, time_range, ... }
    └── { annotation_id, annotation_type, label, time_range, embedding_vector, ... }

Root carries the asset + bucket identity that's the upsert composite key. Child documents carry the per-annotation payload with heterogeneous field shapes across modalities.

Query expressiveness

The canonical query shape this enables:

"assets / buckets where a CHARACTER_SEARCH annotation with label Joey co-occurs with a SCENE_SEARCH annotation with label kitchen"

Expressed as an Elasticsearch nested query with two bool must clauses over the source_annotations field, this returns precisely the buckets where both conditions are matched by children of the same bucket.

Without the nested typing, the two clauses could match separate buckets glued by document boundaries — incorrect for the cross-annotation semantics.

Seen in

Caveats

  • Nested queries are more expensive than flat term/match queries on Elasticsearch — each nested child is a separate Lucene document under the hood, and joins within a parent require the nested block join.
  • Updates to any nested child require reindexing the whole parent document (Elasticsearch doesn't support partial nested-child updates). Netflix's composite-key upsert shape implies full-document replacement on every model re-run.
  • Sharding implications: asset-level data locality depends on the routing key; Netflix doesn't disclose routing strategy.
  • Query-time cost of nested inner_hits on large bucket populations is a known operational concern that the post doesn't address.
Last updated · 319 distilled / 1,201 read