Fuzzy query

Fuzzy query #

Returns documents that contain terms similar to the search term, within the maximum edit distance (Levenshtein — with transpositions counting as a single edit by default).

Use it when the indexed terms are only approximately known: typos in identifiers, OCR noise, or human-entered names.

Inside a nested query: not supported yet — the clause is rejected with an HTTP 400 naming the offending clause.

How it works #

The value is expanded against the inverted index’s term dictionary by edit distance, then the survivors’ postings are unioned like a should of term queries. The engine takes the union of three resolution passes — the value recorded as an exact term, a whole-value fuzzy walk, and (on non-keyword fields) a per-token walk after running the field’s search analyzer over the value — so the query works on keyword fields and on analyzed text alike (the per-token pass matters for CJK and multi-word input). On keyword fields the raw value is used, with the field’s normalizer applied first and no tokenization.

The four knobs — prefix_length, max_expansions, transpositions and case_insensitive — are wired end-to-end across the engine’s execution layers (live, built and frozen segments; the count path honours them exactly like the hits path so totals and results agree):

  • prefix_length requires the first N characters of every expansion term to match the value exactly — a cheap relevance and cost filter.
  • max_expansions (default 50) truncates the candidate set best-distance-first.
  • transpositions (default true) counts an adjacent-character swap (ab → ba) as one edit (Damerau/OSA, the engine’s native distance); false re-checks every candidate with strict Levenshtein, which can drop swap-only matches.
  • case_insensitive lowercases both sides before matching.

Scoring. Each hit scores BM25 × a distance-derived boost of (max_edits + 1 − distance) — exact matches outrank one-edit matches outrank two-edit matches, before BM25 breaks ties.

Examples #

The search below misspells search by one character — serch is a single insertion away, well within 2 edits, so the 25 GET /search lines still match:

"AUTO" picks the budget from the value’s length — serch is 5 characters, so AUTO grants a single edit, which is exactly the insertion needed to reach search:

POST /my-collection/_search
{
  "query": {
    "fuzzy": {
      "field": "user.id",
      "value": "kiiby",
      "fuzziness": 2
    }
  }
}

Use cases #

  • Typo-tolerant lookups. serch over logs.message still finds the 25 GET /search lines — the single-edit case every search box needs.
  • Identity resolution. Customer or host names typed by humans (medcl / medic / mecl) — AUTO with a prefix_length of 1–2 keeps expansions sane on large vocabularies.
  • OCR and sensor noise. Machine-generated text where single-character substitutions are common; transpositions additionally catches the swapped-pair class.
  • Vocabulary suggestion. Run fuzzy over a keyword field with fuzziness: 1 to propose “did you mean” terms before running the real query.

Parameters for fuzzy #

  • field
    (Required, string) Field you wish to search.
  • value
    (Required, string) Term you wish to find in the provided field.
  • fuzziness
    (Optional) Maximum edit distance allowed for matching: an integer 0–2, "AUTO", or "AUTO:low,high". "AUTO" picks the budget from the value’s length at query time — shorter than low characters (default 3): 0 edits; shorter than high characters (default 6): 1 edit; otherwise 2. Values above 2 clamp to 2 (Levenshtein automata are only implemented up to distance 2). Default 0 (exact) — write "AUTO" explicitly for auto-scaling.
  • prefix_length
    (Optional, integer, default: 0) The first N characters of every expansion term must match the query value exactly.
  • max_expansions
    (Optional, integer, default: 50) Cap on the number of expansion terms considered, best-distance-first.
  • transpositions
    (Optional, boolean, default: true) An adjacent-character transposition (ab → ba) counts as one edit. false re-checks each candidate with strict Levenshtein distance.
  • case_insensitive
    (Optional, boolean, default: false) Fold case on both the query value and candidate terms before matching.
  • boost
    (Optional, float, default: 1.0) Decrease or increase the relevance scores of the query.
  • term — the exact-match original; fuzzy with fuzziness: 0 degenerates to it.
  • match — analyzed full-text matching. Note match rejects a fuzziness parameter with HTTP 400; fuzzy matching on analyzed text is this query’s job.
  • query_string — the ~ operator is fuzzy matching inside a query-string expression.
Calendar October 6, 2026
Edit Edit this page