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
nestedquery: 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_lengthrequires the first N characters of every expansion term to match the value exactly — a cheap relevance and cost filter.max_expansions(default50) truncates the candidate set best-distance-first.transpositions(defaulttrue) counts an adjacent-character swap (ab → ba) as one edit (Damerau/OSA, the engine’s native distance);falsere-checks every candidate with strict Levenshtein, which can drop swap-only matches.case_insensitivelowercases 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.
serchoverlogs.messagestill finds the 25GET /searchlines — the single-edit case every search box needs. - Identity resolution. Customer or host names typed by humans
(
medcl/medic/mecl) —AUTOwith aprefix_lengthof 1–2 keeps expansions sane on large vocabularies. - OCR and sensor noise. Machine-generated text where single-character
substitutions are common;
transpositionsadditionally catches the swapped-pair class. - Vocabulary suggestion. Run
fuzzyover a keyword field withfuzziness: 1to 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 providedfield.fuzziness
(Optional) Maximum edit distance allowed for matching: an integer0–2,"AUTO", or"AUTO:low,high"."AUTO"picks the budget from the value’s length at query time — shorter thanlowcharacters (default 3): 0 edits; shorter thanhighcharacters (default 6): 1 edit; otherwise 2. Values above 2 clamp to 2 (Levenshtein automata are only implemented up to distance 2). Default0(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.falsere-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.
Related #
term— the exact-match original;fuzzywithfuzziness: 0degenerates to it.match— analyzed full-text matching. Notematchrejects afuzzinessparameter 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.