Highlighting & Suggesters

intermediate elasticsearch highlighting suggesters autocomplete

Two features that turn raw search into a real product UX: highlighting (showing users why a result matched) and suggesters (autocomplete and typo correction).

Highlighting — “show me what matched”

When we Google something, the matched words are bolded in the snippet. That’s highlighting. Elasticsearch wraps the matched terms in <em> tags (configurable) inside a copy of the field.

GET /articles/_search
{
  "query": {
    "match": { "body": "elasticsearch sharding" }
  },
  "highlight": {
    "fields": {
      "body": {}
    }
  }
}

The response now includes a highlight block per hit:

"highlight": {
  "body": [
    "An intro to <em>elasticsearch</em> <em>sharding</em> and routing..."
  ]
}

Three highlighter types

Think of it like JPEG vs PNG vs SVG — same goal, different tradeoffs.

  • unified (default) — uses Lucene’s UnifiedHighlighter. Works on any text field, decent speed, supports complex queries. Good default.
  • plain — re-runs the query on each field in memory. Most accurate, slowest, OK for small fields like titles.
  • fvh (fast vector highlighter) — needs term_vector: with_positions_offsets in the mapping. Fastest for long fields (think entire article body).
"highlight": {
  "pre_tags": ["<mark>"],
  "post_tags": ["</mark>"],
  "fields": {
    "title": { "type": "plain" },
    "body":  { "type": "fvh", "fragment_size": 150, "number_of_fragments": 3 }
  }
}

fragment_size limits each snippet length; number_of_fragments caps how many we get.

Suggesters — autocomplete and “did you mean?”

Suggesters are a separate API path designed for low-latency single-keystroke responses.

Pick your suggester
term
single-word typo fix. "elasitcsearch" → "elasticsearch".
phrase
whole-phrase correction. "the qick brown fox" → "the quick brown fox".
completion
prefix-as-you-type autocomplete. FST-backed, sub-ms.

Completion suggester (the autocomplete one)

You need a dedicated field of type completion:

PUT /products
{
  "mappings": {
    "properties": {
      "name_suggest": { "type": "completion" }
    }
  }
}

Index docs with input options (and optional weight to bias popular items):

POST /products/_doc
{
  "name_suggest": {
    "input": ["Macbook Pro", "MBP"],
    "weight": 100
  }
}

Query — note this is _search with a top-level suggest block, no query:

POST /products/_search
{
  "suggest": {
    "p-suggest": {
      "prefix": "macb",
      "completion": {
        "field": "name_suggest",
        "size": 5,
        "fuzzy": { "fuzziness": 1 }
      }
    }
  }
}

Behind the scenes the completion suggester stores entries in an FST (finite-state transducer) held entirely in memory. That’s why it’s blisteringly fast but uses RAM — don’t dump millions of entries in without budgeting.

Term suggester (the “did you mean?” one)

POST /articles/_search
{
  "suggest": {
    "did_you_mean": {
      "text": "elasitcsearch sharing",
      "term": { "field": "body" }
    }
  }
}

Term suggester walks the index’s term dictionary using edit distance (Levenshtein) to find close matches. Use it when a regular query returns zero results.

When to use what

  • Showing matched snippets in search results? → highlighting (unified by default).
  • Typeahead in a search box? → completion suggester.
  • “Did you mean…?” prompt? → phrase or term suggester.
  • Need both autocomplete and full-text search on the same field? → index it twice: once as text, once as completion.