Boolean query

Boolean query #

A query that matches documents matching boolean combinations of other queries. It is built using one or more boolean clauses.

Inside a nested query: supported — must/filter/should/must_not and minimum_should_match all apply within ONE array element; clauses matched by different elements never combine.

How it works #

bool compiles each clause list into iterator algebra: must clauses intersect (AND), must_not clauses subtract, filter clauses intersect without scoring, and should clauses are probed per matched document — a streaming two-pointer walk that adds each matching should score on top of the base hit, with no intermediate materialization.

Matching and scoring rules:

  • must — every clause must match; each contributes its score, summed.
  • filter — every clause must match; resolved through the unscoring range/BKD/column-store paths, contributing nothing to the score.
  • must_not — no clause may match; unscored.
  • should — optional score boosts while must/filter do the filtering. They become required in exactly two cases: when the bool has should clauses but no must/filter (then at least one must match), or when minimum_should_match is 1 or more, which turns the should group into a required at-least-N-of-M gate alongside the other clauses.

When several clauses match the same document their scores add — an OR union sums per-document scores, and a union reduced to a single child passes the child’s score through unchanged, so a one-branch bool never distorts the score below it. score_mode selects how multi-field should scores combine — most_fields (sum, the default) or best_fields (dis_max-style: max + tie_breaker * sum(other_scores), with tie_breaker in [0, 1]). Per-clause boost multiplies into the BM25 chain before the combination. One frozen-segment nuance: when a multi-term expansion (wildcard, regexp, prefix) is a should clause of a same-field bool, its expanded terms merge into the field’s term stream and score per-term BM25 there instead of the constant score a standalone expansion receives.

The optimizer picks the execution shape automatically — filter-led vector evaluation when a filter constrains a vector clause, text-driven scoring with vector boosts when a must text query pairs with a should vector, and reciprocal-rank fusion when independent pipelines sit in a should-only bool. Query::simplify also flattens the tree: a bool whose only content is a single must collapses to that query, and a redundant match_all inside must is dropped when other clauses already restrict the set.

Examples #

The query below combines a full-text must with an unscoring range filter — the hats (type = hat) that cost at least 25. All 30 hats are priced 30, so all 30 match:

POST /my-collection/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "field": "title", "query": "search" } }
      ],
      "filter": [
        { "term": { "field": "status", "value": "published" } }
      ],
      "must_not": [
        { "range": { "field": "views", "gte": 100000 } }
      ],
      "should": [
        { "term": { "field": "tags", "value": "featured" } }
      ],
      "minimum_should_match": 1
    }
  }
}

Clause types #

  • must
    The document must match. Contributes to the score.
  • filter
    The document must match. Does not contribute to the score — clauses resolve through the unscoring index paths.
  • should
    The document should match. Contributes to the score; required to match only when no must/filter clauses are present (then at least one should must match) or when minimum_should_match is set.
  • must_not
    The document must not match. Does not contribute to the score.

Use cases #

  • Filter + full text. The workhorse shape: must a match for relevance, filter a range/term for the hard constraints — scored where it matters, unscored where it doesn’t. The example above (hats at price ≥ 25) is exactly this pattern.
  • Exclusion lists. must_not removes archived rows, spam flags or maintenance windows from an otherwise broad query.
  • Optional relevance signals. should boosts featured, recent or vector-similar items without gating admission — and turns into a hard gate via minimum_should_match when the rule demands it.
  • Hybrid retrieval. BM25 text and vector search in one bool: the optimizer fuses the pipelines (text-driven with vector boosts, or RRF for independent shoulds) with no query-side hint.

Parameters for bool #

  • must, filter, should, must_not
    (Optional, array of query objects — a single query object is also accepted as shorthand) The clause lists. At least one clause somewhere is required for the bool to match anything.
  • minimum_should_match
    (Optional, integer, 0–255) Minimum number of should clauses a document must match. The built-in default is 1 when the query has should clauses but no must/filter, otherwise 0. The percentage and offset string forms ("75%", "-1") are not accepted — the value is a plain integer.
  • score_mode
    (Optional, string) Multi-field score combination strategy for should clauses: most_fields (sum — the default, for backward compatibility) or best_fields (max, dis_max style).
  • tie_breaker
    (Optional, float, default: 0) Tie-breaker for best_fields mode. Final score: max_score + tie_breaker * sum(other_scores). Range [0, 1].
  • match / multi_match — the scored full-text leaves most bools combine; multi_match itself expands into a bool.
  • range — the typical filter occupant: unscoring numeric and date restrictions.
  • match_all — the everything base under must_not-only bools and filter-only aggregations.
  • Analyzers — what the analyzed child queries tokenize with.
  • API conventions — clause shorthand and strict-gate parameter handling.
Calendar October 6, 2026
Edit Edit this page