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_offsetsin 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.
single-word typo fix. "elasitcsearch" → "elasticsearch".
whole-phrase correction. "the qick brown fox" → "the quick brown fox".
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 (
unifiedby default). - Typeahead in a search box? →
completionsuggester. - “Did you mean…?” prompt? →
phraseortermsuggester. - Need both autocomplete and full-text search on the same field? → index it twice: once as
text, once ascompletion.