The Elasticsearch multi_match query runs a single piece of input text against multiple fields and combines the results according to a chosen type. It is the standard query for searches that span title, body, tags, and other text fields with different weights. The type parameter (default best_fields) controls how per-field scores combine; field boosts (field^2) and tie_breaker tune relevance further.
Syntax
GET /index/_search
{
"query": {
"multi_match": {
"query": "search text",
"fields": ["title^3", "summary^2", "body"],
"type": "best_fields",
"tie_breaker": 0.3,
"operator": "and"
}
}
}
Parameters
| Parameter | Description | Required | Default |
|---|---|---|---|
query |
Text to search for. | Yes | - |
fields |
Array of fields to search. Supports field^boost and wildcards (name.*). |
No | All fields enabled for the query (*). |
type |
best_fields, most_fields, cross_fields, phrase, phrase_prefix, bool_prefix. |
No | best_fields |
tie_breaker |
Weight applied to non-best-matching fields' scores. Only used by best_fields and most_fields. |
No | 0.0 (only best field counts) |
operator |
or or and, applied per field. |
No | or |
minimum_should_match |
Minimum optional clauses that must match. | No | - |
analyzer |
Override analyzer for the query string. | No | Search analyzer of each field |
fuzziness |
Maximum edit distance (AUTO, 0, 1, 2). Ignored for phrase/phrase_prefix/cross_fields. |
No | - |
prefix_length |
Common prefix kept fixed in fuzzy matching. | No | 0 |
max_expansions |
Max term expansions for fuzzy and prefix matching. | No | 50 |
slop |
Tokens allowed between phrase terms (phrase/phrase_prefix only). | No | 0 |
zero_terms_query |
none or all when the analyzer removes all tokens. |
No | none |
auto_generate_synonyms_phrase_query |
Build phrase queries for multi-term synonyms. | No | true |
lenient |
Ignore type-conversion errors per field. | No | false |
boost |
Score multiplier for the whole query. | No | 1.0 |
Examples
best_fields (default) - one field dominates the score:
GET /articles/_search
{
"query": {
"multi_match": {
"query": "quick brown fox",
"fields": ["title^3", "summary^2", "content"],
"tie_breaker": 0.3
}
}
}
cross_fields - treats sibling fields as one logical field. Useful for name-like data spread across first_name, last_name, email:
GET /users/_search
{
"query": {
"multi_match": {
"query": "will smith",
"type": "cross_fields",
"fields": ["first_name", "last_name", "email"],
"operator": "and"
}
}
}
phrase_prefix - autocomplete on the last term of a phrase:
GET /products/_search
{
"query": {
"multi_match": {
"query": "wireless head",
"type": "phrase_prefix",
"fields": ["title", "description"]
}
}
}
Wildcard field selector with fuzziness:
GET /products/_search
{
"query": {
"multi_match": {
"query": "headphons",
"fields": ["name.*"],
"fuzziness": "AUTO"
}
}
}
Performance and Use Notes
best_fields and most_fields run as one query per field and combine the per-field scores. cross_fields requires that the fields share the same search_analyzer; mismatched analyzers fall back to per-field scoring with a warning. phrase and phrase_prefix are far more expensive because they retain positional information and re-check term order on each candidate.
Wildcards in fields are resolved at query time. Patterns like * or name.* are cheap on schemas with a small mapped field count but expensive on indices with hundreds of fields - each resolved field becomes its own subquery. Combining a wildcard field selector with fuzziness multiplies cost: each field generates up to max_expansions candidate terms (default 50).
Multi-match queries are the workhorse of search APIs and a frequent slow-query offender when field lists or fuzziness are not bounded. Pulse monitors Elasticsearch latency, flags multi_match queries with unbounded field wildcards or excessive expansion, and suggests concrete type/fields rewrites.
Common Mistakes
- Using
cross_fieldswith fields that have different analyzers - the query silently rewrites to per-field scoring and behaves likemost_fields. - Adding
fuzziness: AUTOoncross_fields,phrase, orphrase_prefix- fuzziness is ignored for those types. - Searching
fields: ["*"]on a wide mapping. Restrict to known text fields to keep per-shard work bounded. - Forgetting that
phrase_prefixonly does prefix matching on the last term of the phrase; earlier terms must match exactly. - Relying on the default
tie_breaker: 0, which makesbest_fieldslook like a single-field query - set 0.2-0.4 if you want non-best fields to contribute.
Frequently Asked Questions
Q: What is the difference between multi_match and query_string?
A: multi_match takes plain text and runs it against multiple fields with predictable scoring rules. `query_string` parses Lucene's mini-language (operators, field selectors, regex). Use multi_match for typical user input; use query_string only when callers need full Lucene syntax.
Q: When should I use best_fields vs most_fields vs cross_fields?
A: best_fields wins when the same phrase tends to appear in one field at a time (title or body, not both). most_fields is good when multiple analyzers of the same field contribute evidence (e.g. title + title.stemmed). cross_fields is for name-like data fragmented across siblings with the same analyzer.
Q: What does tie_breaker do in multi_match?
A: With best_fields and most_fields, the final score is best_field_score + tie_breaker * sum(other_field_scores). A value of 0 ignores all but the best field; 1.0 makes the query behave like most_fields. Typical values fall between 0.2 and 0.4.
Q: Can I use wildcards in the fields parameter?
A: Yes. fields: ["name.*"] expands to every sub-field of name at query time. Keep the pattern narrow on indices with hundreds of fields; each matched field becomes its own subquery.
Q: Why is fuzziness ignored in my multi_match query?
A: fuzziness is silently ignored for type: cross_fields, phrase, and phrase_prefix. Switch to best_fields or most_fields (or add an explicit match clause with fuzziness) if you need edit-distance tolerance.
Q: How do I boost one field over others?
A: Append ^N to the field name: "title^3". The boost multiplies that field's contribution to the score. Combine with tie_breaker so non-boosted fields still contribute.
Related Reading
- Elasticsearch Query Language: overview of the query DSL.
- Query String Query: when you need Lucene syntax instead of plain text.
- Bool Query: combine
multi_matchwith filters and exclusions. - Prefix Query: cheaper alternative for autocomplete on a single field.
- Nested Query: apply
multi_matchinside nested objects.