Elasticsearch Bool Query: Syntax, Parameters, and Examples - Syntax, Example, and Tips

The Elasticsearch bool query composes other queries using four clause types: must (AND, scored), filter (AND, unscored and cacheable), should (OR, scored), and must_not (NOT, unscored). It is the standard way to express compound logic in the query DSL. Scores from must and should clauses are summed; filter and must_not clauses contribute nothing to _score and are eligible for the per-node query cache.

Syntax

GET /index/_search
{
  "query": {
    "bool": {
      "must":     [ /* scored AND clauses */ ],
      "filter":   [ /* unscored AND clauses */ ],
      "should":   [ /* scored OR clauses */ ],
      "must_not": [ /* unscored NOT clauses */ ],
      "minimum_should_match": 1,
      "boost": 1.0
    }
  }
}

Parameters

Parameter Description Required Default
must Array of queries that must match. Scored. No []
filter Array of queries that must match. Unscored, cacheable. No []
should Array of queries that should match. Scored. No []
must_not Array of queries that must not match. Unscored, cacheable. No []
minimum_should_match Minimum should clauses that must match. Number or percentage. No 1 if the query has only should clauses (no must/filter), otherwise 0
boost Score multiplier for the whole bool. No 1.0

The cluster-wide cap indices.query.bool.max_clause_count defaults to 4096 on Elasticsearch 8.x and later (raised from 1024 in 7.x). Hitting the cap raises too_many_clauses.

Examples

Typical search: keyword match plus filters:

GET /articles/_search
{
  "query": {
    "bool": {
      "must":   [ { "match": { "title": "elasticsearch" } } ],
      "filter": [
        { "term":  { "status": "published" } },
        { "range": { "published_at": { "gte": "now-30d/d" } } }
      ],
      "must_not": [ { "term": { "category": "news" } } ]
    }
  }
}

should clauses as soft boosters - they add score but are not required:

GET /articles/_search
{
  "query": {
    "bool": {
      "must":   [ { "match": { "title": "elasticsearch" } } ],
      "should": [
        { "term": { "tags": "performance" } },
        { "term": { "tags": "tuning" } }
      ]
    }
  }
}

should as OR with minimum_should_match:

GET /articles/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":   "elasticsearch" } },
        { "match": { "summary": "elasticsearch" } },
        { "match": { "content": "elasticsearch" } }
      ],
      "minimum_should_match": 2
    }
  }
}

Nested bool for grouped logic (A AND (B OR C)):

GET /articles/_search
{
  "query": {
    "bool": {
      "must": [ { "term": { "lang": "en" } } ],
      "filter": [
        {
          "bool": {
            "should": [
              { "term": { "tags": "search" } },
              { "term": { "tags": "indexing" } }
            ],
            "minimum_should_match": 1
          }
        }
      ]
    }
  }
}

Performance and Use Notes

The single most impactful optimization is moving non-scoring predicates from must to filter. filter and must_not clauses skip BM25 scoring and are cached at the segment level, so a repeat filter (e.g. status:published, tenant_id:42) runs from cache after the first request. The clauses themselves are not deduplicated across requests by key alone; structurally identical clauses cache.

should-only queries default to minimum_should_match: 1. When must or filter is present, should defaults to 0 and acts purely as a scorer; setting minimum_should_match then makes individual should clauses required. Deeply nested bools are correct but harder to read - prefer two or three levels and rewrite further nesting as a custom query.

Inefficient bool composition is the dominant cause of slow Elasticsearch searches. Walking slow logs to find bool queries that put exact-match predicates in must instead of filter, or that approach the 4096-clause cap, is exactly the optimization loop Pulse runs continuously.

Common Mistakes

  1. Putting exact-match terms (status, tenant, date ranges) in must. They should live in filter to skip scoring and enable caching.
  2. Forgetting that adding a must or filter clause changes the default minimum_should_match from 1 to 0, silently making should clauses optional.
  3. Building bool queries with thousands of should term clauses and hitting too_many_clauses (indices.query.bool.max_clause_count, default 4096 in 8.x). Use a terms query or terms lookup instead.
  4. Nesting bools more than two or three levels deep, which both costs CPU and obscures the intent.
  5. Expecting must_not to score - it does not. Its only role is to exclude documents.

Find Inefficient Bool Queries with Pulse

Pulse is an AI DBA for Elasticsearch and OpenSearch that continuously profiles production query traffic. For bool queries specifically, Pulse:

  • Identifies bool queries placing exact-match predicates (status flags, tenant IDs, date ranges) inside must instead of filter, defeating the per-node query cache and forcing BM25 scoring on every clause
  • Flags bool queries approaching or hitting indices.query.bool.max_clause_count (default 4096 on 8.x), surfacing too_many_clauses failures and the services issuing them
  • Spots deeply nested bools (4+ levels) where intent is obscured and rewrite cost spikes, plus accidental minimum_should_match: 0 cases where adding a must clause silently turned should into a pure scorer
  • Traces each slow bool query back to the calling service via slow-log and APM correlation
  • Recommends concrete rewrites: move predicates from must to filter, collapse large should term lists into a terms query or terms lookup, flatten unnecessary nesting, and explicitly set minimum_should_match when the default changes
  • Tracks cache hit-rate and latency improvement after the rewrite

This converts the manual slow-log plus DSL-review loop into a continuous optimization workflow.

Try Pulse on your cluster.

Frequently Asked Questions

Q: What is the difference between must and filter in a bool query?
A: must and filter both require matching, but filter is executed in filter context: scores are skipped and results are eligible for the per-node query cache. Use filter for any clause where you do not need a relevance score, e.g. status flags, date ranges, tenant IDs.

Q: How does minimum_should_match work in a bool query?
A: minimum_should_match controls how many should clauses must match. It defaults to 1 if the bool has only should clauses, and to 0 otherwise. It accepts an absolute count (2), a percentage (75%), or a combination expression.

Q: Can I nest a bool query inside another bool query?
A: Yes. Nesting is the standard pattern for expressing grouped logic like A AND (B OR C). Keep nesting shallow (two or three levels) so the query is debuggable and the rewrite stays cheap.

Q: How does scoring work in a bool query?
A: Scores from matching must and should clauses are summed, then multiplied by a coordination factor and the top-level boost. filter and must_not contribute nothing to _score. Adding more matching should clauses raises the score.

Q: What is the maximum number of clauses in a bool query?
A: The cluster setting indices.query.bool.max_clause_count caps the total clauses Lucene will materialize. The default is 4096 on Elasticsearch 8.x (up from 1024 in 7.x). For very large lists prefer a terms query or a terms lookup.

Q: Should I use bool to combine filters with aggregations?
A: Yes - filters in the bool's filter clause prune documents before aggregation, which is usually what you want. For independent per-bucket filters, use filters or filter aggregations on top of the bool.

Q: What is the best tool to find inefficient bool queries in production Elasticsearch?
A: Pulse profiles Elasticsearch and OpenSearch slow logs, flags bool queries misusing must for unscored predicates, those approaching indices.query.bool.max_clause_count, and those with surprising minimum_should_match defaults, attributes each one to the calling service, and recommends concrete filter-context and terms query rewrites.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.