The Elasticsearch dis_max (disjunction max) query takes a set of sub-queries and returns documents matching at least one. Its scoring uses only the highest-scoring sub-query, optionally blended with the others via a tie_breaker. It works on any field type that the wrapped sub-queries support. Use dis_max when the same user input is run against several fields and the right answer is "score by the single best field", not "sum scores across fields".
Syntax
{
"query": {
"dis_max": {
"queries": [ /* sub-queries */ ],
"tie_breaker": 0.0,
"boost": 1.0
}
}
}
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
queries |
array | - | One or more sub-queries (any DSL clause). At least one is required. |
tie_breaker |
float 0.0-1.0 |
0.0 |
Fraction of non-max sub-query scores added to the max. 0.0 = pure best-field; higher values reward documents that match multiple sub-queries. |
boost |
float | 1.0 |
Multiplier on the final score. |
multi_match with type: best_fields (the default) is internally rewritten to a dis_max. Use dis_max directly when the per-field sub-queries differ in shape (different analyzers, different fuzziness, mixing match and match_phrase, etc.).
Examples
Best-field search across title, body, and tags:
GET /articles/_search
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "elasticsearch guide" } },
{ "match": { "body": "elasticsearch guide" } },
{ "match": { "tags": "elasticsearch guide" } }
],
"tie_breaker": 0.3
}
}
}
Mix query types per field - match_phrase on title for higher weight, match on body for recall:
GET /articles/_search
{
"query": {
"dis_max": {
"queries": [
{ "match_phrase": { "title": { "query": "distributed search", "boost": 3 } } },
{ "match": { "body": "distributed search" } }
],
"tie_breaker": 0.2
}
}
}
Inside a bool - filter on category, score by best-field across language-specific fields:
GET /docs/_search
{
"query": {
"bool": {
"filter": [ { "term": { "category": "search" } } ],
"must": [
{ "dis_max": {
"queries": [
{ "match": { "body.english": "running shoes" } },
{ "match": { "body.spanish": "running shoes" } },
{ "match": { "body.shingles": "running shoes" } }
],
"tie_breaker": 0.1
}}
]
}
}
}
Performance and Use Notes
Each sub-query runs independently and the engine takes the max. Cost is roughly the sum of sub-query costs. dis_max is cheap when the inner clauses are cheap (term, match against keyword), and inherits whatever overhead the inner clauses have (phrase positions, fuzzy expansion, wildcards).
tie_breaker is the key tuning knob. With 0.0, score equals the single highest sub-query score; documents that match two fields look identical to documents that match only one. With 1.0, scores sum and dis_max behaves like a bool.should. Typical production values are 0.1-0.3: prefer the best field but reward multi-field matches a little.
When the per-field sub-queries are structurally identical, multi_match is shorter, easier to maintain, and produces the same plan. Pick dis_max only when sub-queries diverge in shape.
Multi-field scoring choices have outsized impact on perceived search quality. Pulse inspects Elasticsearch query patterns and surfaces dis_max/multi_match configurations where tie_breaker choice is inconsistent with relevance goals - useful when relevance regressions need triage without rebuilding indexes.
Common Mistakes
- Setting
tie_breaker: 1.0and effectively replacingdis_maxwithbool.should- the disjunction-max property is lost. - Forgetting that
dis_maxis a scoring query; placing it inbool.filterdiscards score and removes the entire benefit. - Mixing fields of very different lengths without per-clause
boost- the longer field dominates pure-BM25 scoring anddis_maxkeeps picking it. - Using
dis_maxwith one sub-query - it is a no-op wrapper; remove it. - Reaching for
dis_maxwhenmulti_matchwithtype: best_fieldswould express the same thing more concisely.
Frequently Asked Questions
Q: How does the dis_max query differ from a bool should query?
A: bool.should sums scores from every matching clause; dis_max takes the maximum score and optionally adds a tie_breaker * sum_of_others. Use dis_max when the right answer is "score by the best field", not "score by total signal across fields".
Q: What does tie_breaker control in a dis_max query?
A: tie_breaker is the fraction (0.0-1.0) of non-max sub-query scores added to the max. 0.0 is pure best-field. 1.0 is equivalent to summing all sub-query scores. Production setups typically use 0.1-0.3.
Q: When should I use dis_max instead of multi_match?
A: Use dis_max directly when the per-field sub-queries differ in shape - different query types, different analyzers, different fuzziness. When every field gets the same match clause, multi_match with type: best_fields is the shorter form and compiles to the same dis_max plan internally.
Q: Does dis_max support filters?
A: No, not as a direct parameter. Filters should be applied by wrapping dis_max inside a bool query and placing term/range clauses in bool.filter.
Q: Can dis_max be combined with function_score?
A: Yes. Wrap the dis_max clause inside a function_score query as the inner query, then apply functions (decay, popularity, script_score) on top of the best-field score.
Q: Why are my dis_max results dominated by one field?
A: Usually because that field has shorter documents and BM25 favors short fields, or because per-clause boost skews the comparison. Inspect with "explain": true to see each sub-query's contribution, then adjust per-field boost values.
Related Reading
- Elasticsearch Bool Query: score-sum alternative to dis_max.
- Elasticsearch Multi Match Query: the canonical "same query against many fields" wrapper.
- Elasticsearch Match Query: the typical sub-query inside dis_max.
- Elasticsearch Function Score Query: post-rerank a dis_max result.
- Elasticsearch Term Query: exact-match clause for filters around dis_max.