Full-text queries

This section documents the current Elasticsearch 9.x line (with 8.19 as the final 8.x release) as published at the Elasticsearch documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. Some capabilities (Kibana-only UIs, the ML/NLP model-management workflow, cross-cluster replication, and parts of the paid / serverless-only surface) are linked, not documented in depth.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, as Elasticsearch iterates quickly.

This section’s bibliography lists the reference material consulted while preparing these pages.

Full-text queries run against text fields: the query string is analyzed into terms the same way the field was at index time, then those terms are looked up in the inverted index and the matches are scored with BM25. This page covers the match family, multi_match and combined_fields, and the query_string / simple_query_string / intervals queries. For exact, non-analyzed matching on keyword, numbers and dates, see Term-level queries; for how these clauses combine and score, see Compound queries & relevance. Overview: Full text queries.

The search text is analyzed like the field

A full-text query is not a substring match. Elasticsearch passes the query string through an analyzer — by default the field’s search_analyzer (falling back to its analyzer) — so "The Quick Foxes" becomes [quick, fox] if the field uses the english analyzer, and those terms are what get matched. This is why the same words find a document whether they were capitalized, pluralized or reordered. See Text analysis for how analyzers are built and tested, and Text analysis for the reference.

PUT /articles
{
  "mappings": {
    "properties": {
      "title":   { "type": "text", "analyzer": "english" },
      "body":    { "type": "text" },
      "tag":     { "type": "keyword" }
    }
  }
}

PUT /articles/_doc/1?refresh
{ "title": "The Quick Brown Foxes", "body": "A story about clever animals", "tag": "wildlife" }

# "foxes" is stemmed to "fox" by the english analyzer, so this matches.
GET /articles/_search
{ "query": { "match": { "title": "fox" } } }

If a full-text query returns nothing you expected, run the analyzer on both the field text and the query string with the _analyze API and compare the terms — a mismatch there is the usual cause.

GET /articles/_analyze
{ "analyzer": "english", "text": "The Quick Brown Foxes" }

match

match is the standard full-text query for a single field. It analyzes the input, then builds a boolean query over the resulting terms — by default any one term matching is enough (OR). See Match query.

GET /articles/_search
{ "query": { "match": { "body": "clever animals" } } }

operator

Set operator to and to require every analyzed term to be present.

GET /articles/_search
{
  "query": {
    "match": {
      "body": { "query": "clever animals", "operator": "and" }
    }
  }
}

minimum_should_match

With the default OR operator you can still demand that a fraction or count of the terms match. minimum_should_match accepts an integer (3), a negative integer (-1 = all but one), a percentage ("75%"), or a combined expression ("2<75%" = if there are more than 2 terms, require 75%). See minimum_should_match parameter.

GET /articles/_search
{
  "query": {
    "match": {
      "body": { "query": "a story about clever wild animals", "minimum_should_match": "75%" }
    }
  }
}

fuzziness

fuzziness allows edit-distance (Levenshtein) matches for typos. "AUTO" picks 0/1/2 edits based on term length and is the recommended setting; prefix_length protects the first N characters from edits and keeps the term expansion cheap.

GET /articles/_search
{
  "query": {
    "match": {
      "title": { "query": "quik browne", "fuzziness": "AUTO", "prefix_length": 1 }
    }
  }
}

analyzer

analyzer overrides which analyzer processes the query string (the field’s own analyzer still governs the indexed terms). Use it when the search-time tokenization should differ, e.g. a standard analyzer for input that must not be stemmed.

GET /articles/_search
{
  "query": {
    "match": {
      "title": { "query": "Foxes", "analyzer": "standard" }
    }
  }
}

match_phrase

match_phrase requires the analyzed terms to appear in order and adjacent (subject to slop, the number of positional moves allowed). It needs the field to index positions, which text does by default. See Match phrase query.

GET /articles/_search
{
  "query": {
    "match_phrase": {
      "body": { "query": "clever animals", "slop": 1 }
    }
  }
}

match_phrase_prefix

match_phrase_prefix treats the last term as a prefix — useful for "search as you type" against a normal text field. It expands the last term to at most max_expansions (default 50) matching terms, so results near the end of the alphabet can be missed; for real typeahead prefer the search_as_you_type field type. See Match phrase prefix query.

GET /articles/_search
{
  "query": {
    "match_phrase_prefix": {
      "title": { "query": "quick bro", "max_expansions": 20 }
    }
  }
}

multi_match

multi_match runs a match against several fields and combines the scores. The type parameter decides how. See Multi-match query.

GET /articles/_search
{
  "query": {
    "multi_match": {
      "query": "clever foxes",
      "fields": [ "title^3", "body" ],
      "type": "best_fields"
    }
  }
}

Field names accept wildcards ("fields": [ "*_name" ]) and per-field boosts (title^3).

type Behaviour

best_fields

(default) Score each field separately, take the single best field’s score (plus tie_breaker times the rest). Best when the words are expected together in one field.

most_fields

Sum the per-field scores. Best when the same text is indexed several ways (e.g. title and title.english) and each analyzer should contribute.

cross_fields

Treat the listed fields as one big field, term-centric: each query term must match in some field (honours operator / minimum_should_match across fields). Best for entities split across fields like first_name + last_name. Requires the grouped fields to share an analyzer.

phrase

Like best_fields but runs match_phrase per field.

phrase_prefix

Like best_fields but runs match_phrase_prefix per field.

bool_prefix

Like most_fields but the last term of each field is a prefix (match_bool_prefix); pairs with a search_as_you_type field.

# Entity spread across two fields: "will smith" should match first_name=Will, last_name=Smith.
GET /people/_search
{
  "query": {
    "multi_match": {
      "query": "will smith",
      "type": "cross_fields",
      "fields": [ "first_name", "last_name" ],
      "operator": "and"
    }
  }
}

combined_fields

combined_fields is the modern term-centric multi-field query: it models the listed fields as one combined field with a principled BM25 score across them, and supports operator and minimum_should_match over the whole set. All fields must share the same search analyzer. Prefer it over cross_fields for new work. See Combined fields query.

GET /articles/_search
{
  "query": {
    "combined_fields": {
      "query": "clever wild animals",
      "fields": [ "title", "body" ],
      "operator": "and"
    }
  }
}

query_string and simple_query_string

query_string parses a compact query language in the string itself: field prefixes (title:fox), booleans (quick AND (brown OR red)), phrases ("quick brown"), wildcards (qu?ck, bro*), regex (/joh?n/), proximity ("fox quick"~5), boosts (fox^2) and ranges (age:[18 TO 30]). A syntax error in the string fails the whole request. Because of that, expose it only to trusted callers (an internal console, a power-user search bar) — never pass raw end-user input to it. See Query string query.

GET /articles/_search
{
  "query": {
    "query_string": {
      "query": "(quick OR clever) AND animals",
      "fields": [ "title^2", "body" ],
      "default_operator": "AND"
    }
  }
}

simple_query_string is the version safe for a public search box: the same mini-language in a simpler form (+ for AND, | for OR, - for NOT, "…​" for phrase, * suffix for prefix, ~N for fuzziness / slop), and it silently ignores malformed parts instead of erroring. You can disable individual operators via flags. See Simple query string query.

GET /articles/_search
{
  "query": {
    "simple_query_string": {
      "query": "clever + animals -boring \"wild animals\"",
      "fields": [ "title", "body" ],
      "default_operator": "and",
      "flags": "AND|OR|NOT|PHRASE|PREFIX"
    }
  }
}

Rule of thumb: use match / multi_match / combined_fields for application-built queries, reach for simple_query_string only when users must type operators themselves, and reserve query_string for internal tools. The Kibana Query Language (KQL) and the classic Lucene query syntax are covered separately in Query languages & scripting.

intervals

The intervals query matches ordered sequences of terms with fine control over gaps and ordering — more expressive than phrase/slop and evaluated per field. Rules like match, prefix, wildcard, fuzzy are composed with all_of (ordered, all required), any_of (alternatives), and filters such as max_gaps and ordered. Use it for "these words, in this order, within N words of each other" requirements that match_phrase slop cannot express cleanly. See Intervals query.

GET /articles/_search
{
  "query": {
    "intervals": {
      "body": {
        "all_of": {
          "ordered": true,
          "max_gaps": 3,
          "intervals": [
            { "match": { "query": "clever" } },
            { "match": { "query": "animals" } }
          ]
        }
      }
    }
  }
}

Where to go next