Nested query

Nested query #

Matches parent documents whose nested sub-documents — objects mapped with "nested": true — satisfy a query within one element at a time.

The cross-element problem #

A flattened index correlates across array elements, not within them: variants.color = blue AND variants.size = L matches a document as soon as some element is blue and some element is L — not necessarily the same one. The teaching dataset’s products makes the difference visible (each element pairs blue with L, never with M):

The flattened query above reports 10 documents — every product with some blue variant and some M variant, even when they are different elements. The nested query below evaluates each variant as a whole element and reports 0: in this dataset blue variants only ever come in size L.

Same-element matching #

When the pair lives in one element, only the nested query is guaranteed correct (blue always pairs with L in this dataset):

20 of 30 products have a blue/L variant. Any leaf composes inside the inner query:

Inner queries and minimum_should_match #

The inner query supports:

Not supported inside nested yet: terms, prefix, wildcard, fuzzy, regexp, suffix, the span family and query_string. These are rejected with an HTTP 400 naming the offending clause and the nested path — never a silent zero-hit answer. The same shapes keep working at the top level.

bool.minimum_should_match applies within one element: a parent matches when at least one element satisfies that many should clauses by itself. Clauses matched by different elements never add up — minimum_should_match: 2 over two should clauses is the same-element AND:

The same 20 blue/L products as the must example above — with minimum_should_match dropped to 1 (the default when the bool has no must/filter) it becomes the same-element OR.

Phrases compose with the same element scoping — the adjacent word pair must live in ONE element, and slop (default 0) buys holes between the words, in query order:

POST /my-collection/_search
{
  "query": {
    "nested": {
      "path": "reviews",
      "query": {
        "match_phrase": {
          "field": "reviews.body",
          "query": "quick fox",
          "slop": 1
        }
      }
    }
  }
}

quick fox with slop: 1 matches an element holding “quick brown fox” (one word between); at slop: 0 nothing matches unless the words are adjacent. Reversed word order ("fox quick") never matches at any slop — phrase terms keep query order.

Schema requirement #

Declare the object as nested so elements are indexed with ordinal-tagged postings (server-side collections). Both forms are accepted — they are the same schema:

PUT /my-collection
{
  "schema": {
    "properties": {
      "variants": {
        "type": "nested",
        "properties": {
          "color": { "type": "keyword" },
          "size": { "type": "keyword" }
        }
      }
    }
  }
}

The explicit form spells the parameter out ("type": "object" + "nested": true) for readers who prefer it.

Documents index variants as an array of objects; each element is addressable through its dotted sub-fields inside the nested query.

Performance characteristics #

Correlation is resolved in one of two ways, depending on where the matching documents live:

  • Built segments — every element’s postings carry a (doc_id, ordinal) pair, and the engine intersects / unions on that composite key. Nested queries resolve at close to ordinary bool cost.
  • Realtime (not yet frozen) documents — the inner query is evaluated against each stored array element, one document at a time. Exact and available immediately after the write, but the cost is linear in documents × elements: a nested query over 20 000 documents × 5 elements measured roughly 10–15× the latency of the equivalent flattened bool (sub-second either way). Keep realtime buffers bounded when nested queries dominate a large collection.

Scoring note: realtime-layer elements match with a constant score, so score_mode only differentiates on built segments.

Parameters for nested #

  • path
    (Required, string) Path of the nested object (e.g. "variants").
  • query
    (Required, object) The query applied to each inner element; a parent matches when at least one element satisfies it.
  • score_mode
    (Optional, string) How per-element scores collapse into the parent’s score: sum (default), avg, min, max, or none (constant 1.0). Note: Elasticsearch defaults to avg — Pizza defaults to sum.
Calendar September 27, 2026
Edit Edit this page