﻿---
title: ES|QL HIGHLIGHT command
description: 
url: https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/commands/highlight
products:
  - Elasticsearch
---

# ES|QL HIGHLIGHT command
<applies-to>
  - Elastic Cloud Serverless: Preview
  - Elastic Stack: Planned
</applies-to>

The `HIGHLIGHT` [processing command](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/commands/processing-commands)
extracts and highlights matching text snippets from one or more fields based on a
full-text query. Matching terms are wrapped in highlight tags, bringing the
highlighting features of the Elasticsearch
[`_search` API](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/elasticsearch/rest-apis/highlighting) to ES|QL.

## Syntax

```esql
HIGHLIGHT [prefix = "<prefix>"] [query] [ON field [, field, ...] | ON *] [WITH { "option": value [, ...] }]
```


## Parameters

<definitions>
  <definition term="prefix">
    (Optional) A quoted string literal used to name the output columns. Each
    highlighted field is written to `<prefix><field>`. Defaults to `highlight_` (for
    example, `HIGHLIGHT "fox" ON content` produces `highlight_content`). If a generated
    column name matches an existing column, the existing column is replaced. To
    overwrite the source column in place, specify an empty prefix (`prefix = ""`).
    Unlike the query and the `WITH` option values, `prefix` cannot be a query parameter.
  </definition>
  <definition term="query">
    (Optional) The query used to find matching terms to highlight. This can be a
    string literal (which uses
    [`query_string`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/query-dsl/query-dsl-query-string-query)
    syntax) or a full-text search function such as
    [`MATCH`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/match),
    [`MATCH_PHRASE`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/match_phrase),
    [`QSTR`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/qstr),
    [`KQL`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/kql),
    or the [match operator `:`](/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/operators#esql-match-operator).
    You can combine full-text functions using `AND`, `OR`, and `NOT`.
    If you don't specify a query, `HIGHLIGHT` automatically reuses full-text
    search conditions from earlier [`WHERE`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/commands/where)
    commands in the query. Refer to [Reuse a query from WHERE](#esql-highlight-implicit-query).
    When you provide both a query and an `ON` clause, any field named in your
    query must also be listed in `ON`. For example,
    `HIGHLIGHT MATCH(title, "fox") ON body` is rejected because `title` is not in
    `ON`. When you let ES|QL determine the query or fields automatically, it
    handles this check for you.
    Unqualified string literals and `QSTR` expressions are evaluated against
    whichever fields are being highlighted. Queries without positive search
    conditions (such as `NOT MATCH(...)`) have no terms to highlight and return
    `null`, unless you configure `no_match_size`.
  </definition>
  <definition term="field">
    (Optional) One or more comma-separated columns to highlight, or `*` to
    highlight every `text` and `keyword` column in the table. Fields must be `text`
    or `keyword` types (`semantic_text` fields are supported and treated as `text`).
    You can only use `*` by itself; wildcard patterns like `title*` and combining
    `*` with specific field names (such as `ON *, title`) are not supported.
    If you omit `ON`, `HIGHLIGHT` determines which columns to highlight based on
    your query:
    - For queries targeting a specific column (such as `MATCH` or `MATCH_PHRASE`),
      only that column is highlighted.
    - For queries that don't target a single column (such as string literals,
      `QSTR`, or `KQL`), `HIGHLIGHT` checks all `text` and `keyword` columns in
      the table.
    Refer to [Choose fields with ON](#esql-highlight-on-fields). If a field has no
    matching terms, its output is `null` unless you set `no_match_size`.
  </definition>
</definitions>


## WITH options

All option values passed in the `WITH` clause must be constants. Both literals and
[query parameters](/elastic/docs-builder/docs/4300/reference/query-languages/esql/esql-rest#esql-rest-params) that
resolve to a literal are accepted; column references are not.
<definitions>
  <definition term="pre_tags">
    (Optional) Opening tag inserted before each highlighted term. Accepts a string
    or a single-element array of strings. Defaults to `<em>`. Multiple rotating
    tags are not supported.
  </definition>
  <definition term="post_tags">
    (Optional) Closing tag inserted after each highlighted term. Accepts a string
    or a single-element array of strings. Defaults to `</em>`.
  </definition>
  <definition term="encoder">
    (Optional) Text encoding applied before adding highlight tags. Accepts
    `default` (no encoding) or `html` (HTML-escapes snippet text). Defaults to
    `default`. As in the `_search` API, this value is case-sensitive, so `html` is valid
    but `HTML` is rejected. `boundary_scanner` and `order` are case-insensitive.
  </definition>
  <definition term="analyzer">
    (Optional) Analyzer used on both the query and field text. Defaults to the
    `standard` analyzer. Only built-in and node-level plugin analyzers are
    supported. If a full-text search function specifies its own `analyzer`, it
    must match the analyzer specified here.
  </definition>
  <definition term="number_of_fragments">
    (Optional) Maximum number of snippets (fragments) to return per field. Set to `0` to return the entire
    field value with matching terms highlighted without fragmenting. Must be `>= 0`.
    Defaults to `5`.
  </definition>
  <definition term="fragment_size">
    (Optional) Approximate character length of each snippet. Must be `>= 0`.
    Defaults to `100`.
  </definition>
  <definition term="no_match_size">
    (Optional) Approximate number of leading characters to return from the field
    when there are no matching terms. This is a minimum, not an exact limit: the
    returned text extends to the next boundary set by `boundary_scanner`, so the
    result can be longer than the requested size. Must be `>= 0`. Defaults to `0`
    (returns `null`).
  </definition>
  <definition term="boundary_scanner">
    (Optional) Boundary scanner used to split text into fragments. Accepts
    `sentence` or `word`, case-insensitively. Defaults to `sentence`.
  </definition>
  <definition term="boundary_scanner_locale">
    (Optional) Locale used by the boundary scanner, given as an
    [IETF BCP 47](https://www.rfc-editor.org/info/bcp47) language tag such as `en-US` or
    `ja-JP`. Use hyphens as separators. Defaults to the root locale. This is the same
    format accepted by the `_search` API's
    [`boundary_scanner_locale`](/elastic/docs-builder/docs/4300/reference/elasticsearch/rest-apis/highlighting-settings#boundary_scanner_locale).
  </definition>
  <definition term="order">
    (Optional) Sort order of returned fragments. Accepts `none` (preserves document
    order) or `score` (orders fragments by descending relevance score),
    case-insensitively. Defaults to `none`.
  </definition>
  <definition term="max_analyzed_offset">
    (Optional) Maximum number of characters to analyze per field value. Accepts a
    positive integer, or `-1` to leave the limit unset. Defaults to `-1`.
    `HIGHLIGHT` analyzes at most 1 million characters per field value regardless of
    this setting, and the index's `index.highlight.max_analyzed_offset` setting does
    not apply. Text beyond the effective offset is not highlighted.
  </definition>
</definitions>


## Description

Use `HIGHLIGHT` to find and display matching snippets in text fields, typically
after filtering rows with a full-text search condition in `WHERE`.
`HIGHLIGHT` processes each row, analyzes the specified text fields against the
query, and generates new keyword columns containing matching terms wrapped in
highlight tags. By default, output columns are named `highlight_<field>`. If a
field contains no matching terms, the result is `null` unless you specify
`no_match_size`.
Because `HIGHLIGHT` re-analyzes text values at query time, you can highlight
source fields from an index as well as computed columns created by earlier
commands like `EVAL`, `DISSECT`, `GROK`, `STATS`, `ENRICH`, or `LOOKUP JOIN`.
For multivalued fields, each value is highlighted independently:
- Phrase queries and fragment boundaries do not cross values.
- When a field produces multiple fragments, the output column contains a multivalued list of snippets.
- Multivalued `keyword` fields loaded from doc values are sorted and deduplicated before highlighting, which can result in a different snippet order compared to the `_search` API.


### Reuse a query from WHERE

Most search queries filter rows with a full-text condition in `WHERE`, then
highlight matching terms in those same fields. To avoid repeating your search
query, you can omit the query from `HIGHLIGHT`. When you do, `HIGHLIGHT`
automatically finds and reuses full-text search conditions from earlier
[`WHERE`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/commands/where) commands.
This works with any positive full-text search function, including
[`MATCH`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/match),
[`MATCH_PHRASE`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/match_phrase),
[`QSTR`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/qstr),
[`KQL`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/kql),
and the match operator `:`.
You can include intermediate commands between `WHERE` and `HIGHLIGHT` as long as
each row still represents an individual document. For example, commands like
`KEEP`, `DROP`, `RENAME`, `EVAL`, `GROK`, `DISSECT`, `LIMIT`, `SORT`,
`MV_EXPAND`, and `INLINE STATS` pass through without issue.
However, commands that summarize, aggregate, or join rows—such as `STATS`,
`LOOKUP JOIN`, or `FORK`—change the document context. If you use any of these
commands between `WHERE` and `HIGHLIGHT`, you must provide the query explicitly
in `HIGHLIGHT`.
If your query contains multiple `WHERE` clauses, `HIGHLIGHT` combines all of
their full-text search conditions so that every searched field can produce
snippets, even though the `WHERE` clauses filter your rows together using `AND`.
The following search conditions cannot be automatically reused:
- Negated conditions, such as `NOT MATCH(...)` (there are no positive matches to highlight)
- Conditions combined with non-text filters using `OR`, such as `MATCH(title, "fox") OR year > 2020`

If your query relies solely on conditions that cannot be reused, specify the
query explicitly in `HIGHLIGHT`.
If you provide an explicit query in `HIGHLIGHT`, it takes precedence, and any
conditions from earlier `WHERE` commands are ignored for highlighting.

### Choose fields with ON

The `ON` clause specifies which columns to highlight. You can choose specific
columns, highlight all available text columns, or let ES|QL determine the
columns automatically:
- **Highlight specific fields**: Use `ON field1, field2` to highlight only the
  specified columns.
- **Highlight all text and keyword fields**: Use `ON *` to highlight every
  `text` and `keyword` column in the current table, including multi-fields
  (such as `author.keyword`) and `semantic_text` fields (highlighted lexically).
  Metadata columns such as `_id` and `_index` are not included.
- **Let ES|QL determine fields**: If you omit `ON`, `HIGHLIGHT` chooses the
  columns based on your query:
  - If the query targets a specific field (such as `MATCH(title, "fox")`),
  only that field is highlighted.
- If the query does not name a specific field (such as a string literal,
  `QSTR`, or `KQL`), `HIGHLIGHT` checks all `text` and `keyword` columns in
  the table.

If a highlighted field does not match any query terms, its output is `null`
(or the leading text specified by `no_match_size`). If ES|QL cannot find any
eligible `text` or `keyword` columns to highlight, you must provide an explicit
`ON` clause.
<tip>
  Learn more about using [ES|QL for search use cases](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/4300/solutions/search/esql-for-search).
</tip>


## Limitations

- `HIGHLIGHT` re-analyzes text with the `standard` analyzer by default, rather than the analyzer configured in the index mapping. If your field uses a custom or language analyzer, specify it with the `analyzer` option in the `WITH` clause.
- The `analyzer` option only supports built-in and node-level plugin analyzers. Analyzers configured in index settings are not supported.
- On `keyword` fields, `HIGHLIGHT` tokenizes text and breaks it into snippets like a text field, rather than treating the value as a single term.
- On `semantic_text` fields, `HIGHLIGHT` performs lexical matching against the underlying text. Semantic vector matches without literal keyword overlap are not highlighted.
- Fields are analyzed up to a maximum of 1 million characters. Text beyond this limit is not analyzed or highlighted.
- `HIGHLIGHT` cannot automatically reuse a `WHERE` query across commands that aggregate, summarize, or join rows, such as `STATS`, `LOOKUP JOIN`, or `FORK`. In those queries, specify the query directly on `HIGHLIGHT`.
- If you drop a field targeted by the reused `WHERE` query before `HIGHLIGHT`, the implicit query can no longer highlight that field. If no other reusable fields remain, provide an explicit query and `ON` clause using columns that are still in scope.


## Examples

The following examples show common ways to highlight search terms and customize snippet output.

### Highlight matches in a field

Wrap matching terms in the default `<em>` tags:
```esql
ROW content = "The quick brown fox jumps over the lazy dog."
| HIGHLIGHT "fox" ON content
| KEEP highlight_content
```


| highlight_content:keyword                             |
|-------------------------------------------------------|
| The quick brown <em>fox</em> jumps over the lazy dog. |


### Highlight search results

Filter rows with a `WHERE` clause, then highlight matching terms in the output.
You can specify the search condition again in `HIGHLIGHT`:
```esql
FROM books
| WHERE MATCH(title, "Return")
| HIGHLIGHT "return" ON title
| KEEP book_no, highlight_title
| SORT book_no
```


| book_no:keyword | highlight_title:keyword                                                   |
|-----------------|---------------------------------------------------------------------------|
| 2714            | <em>Return</em> of the King Being the Third Part of The Lord of the Rings |
| 7350            | <em>Return</em> of the Shadow                                             |


### Automatically reuse a WHERE condition

To avoid repeating your search query, omit the query from `HIGHLIGHT`. When you
also omit `ON`, `HIGHLIGHT` automatically highlights matches in the field
searched by `WHERE` (in this case, creating `highlight_title`):
```esql
FROM books
| WHERE MATCH(title, "Return")
| HIGHLIGHT
| KEEP title, highlight_title
| SORT title
```


| title:text                                                       | highlight_title:keyword                                                   |
|------------------------------------------------------------------|---------------------------------------------------------------------------|
| Return of the King Being the Third Part of The Lord of the Rings | <em>Return</em> of the King Being the Third Part of The Lord of the Rings |
| Return of the Shadow                                             | <em>Return</em> of the Shadow                                             |

To reuse the `WHERE` condition but choose which columns to highlight, provide
an explicit `ON` clause:
```esql
FROM books
| WHERE MATCH(title, "Return")
| HIGHLIGHT ON title
| KEEP book_no, highlight_title
| SORT book_no
```


| book_no:keyword | highlight_title:keyword                                                   |
|-----------------|---------------------------------------------------------------------------|
| 2714            | <em>Return</em> of the King Being the Third Part of The Lord of the Rings |
| 7350            | <em>Return</em> of the Shadow                                             |


### Highlight without an ON clause

When your query targets a specific field (such as `MATCH`), you can omit `ON`.
Only that field is highlighted, leaving other columns untouched:
```esql
ROW title = "Return of the King", body = "An ordinary description."
| HIGHLIGHT MATCH(title, "king")
```


| title:keyword      | body:keyword             | highlight_title:keyword     |
|--------------------|--------------------------|-----------------------------|
| Return of the King | An ordinary description. | Return of the <em>King</em> |

When you use a query that doesn't target a specific field, such as a string
literal or `QSTR`, omitting `ON` highlights all `text` and `keyword` columns:
```esql
ROW title = "Return of the King", body = "Tolkien wrote the epic saga."
| HIGHLIGHT "tolkien"
| KEEP highlight_title, highlight_body
```


| highlight_title:keyword | highlight_body:keyword                |
|-------------------------|---------------------------------------|
| null                    | <em>Tolkien</em> wrote the epic saga. |


### Highlight all text and keyword fields with ON *

Use `ON *` to highlight every `text` and `keyword` column in the table at once.
Columns that do not match the query evaluate to `null`:
```esql
ROW title = "Return of the King", body = "An ordinary description."
| HIGHLIGHT MATCH(title, "king") ON *
| KEEP highlight_title, highlight_body
```


| highlight_title:keyword     | highlight_body:keyword |
|-----------------------------|------------------------|
| Return of the <em>King</em> | null                   |


### Highlight phrases with MATCH_PHRASE

Use a full-text function like `MATCH_PHRASE` to highlight an exact phrase in a single tag pair:
```esql
FROM books
| WHERE MATCH(title, "Return")
| HIGHLIGHT MATCH_PHRASE(title, "Return of the") ON title
| KEEP book_no, highlight_title
| SORT book_no
```


| book_no:keyword | highlight_title:keyword                                                   |
|-----------------|---------------------------------------------------------------------------|
| 2714            | <em>Return of the</em> King Being the Third Part of The Lord of the Rings |
| 7350            | <em>Return of the</em> Shadow                                             |


### Highlight with query string syntax (QSTR)

Use [`QSTR`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/qstr) to highlight terms using Lucene query syntax with boolean operators and field qualifiers:
```esql
ROW title = "The quick fox", body = "A loyal dog"
| HIGHLIGHT QSTR("title:fox OR body:dog") ON title, body
| KEEP highlight_title, highlight_body
```


| highlight_title:keyword | highlight_body:keyword |
|-------------------------|------------------------|
| The quick <em>fox</em>  | A loyal <em>dog</em>   |


### Highlight with Kibana Query Language (KQL)

Use [`KQL`](https://www.elastic.co/elastic/docs-builder/docs/4300/reference/query-languages/esql/functions-operators/search-functions/kql) to highlight terms using Kibana Query Language syntax, optionally combined with other full-text functions:
```esql
FROM books
| WHERE MATCH(title, "Return")
| HIGHLIGHT KQL("title: shad*") OR (MATCH(title, "return") AND MATCH(title, "king")) ON title
| KEEP book_no, highlight_title
| SORT book_no
```


| book_no:keyword | highlight_title:keyword                                                            |
|-----------------|------------------------------------------------------------------------------------|
| 2714            | <em>Return</em> of the <em>King</em> Being the Third Part of The Lord of the Rings |
| 7350            | Return of the <em>Shadow</em>                                                      |


### Highlight with a language analyzer

Use the `analyzer` option to apply language-specific stemming rules. In this example, the `english` analyzer stems `Rings` to `ring`:
```esql
ROW title = "The Lord of the Rings"
| HIGHLIGHT "ring" ON title WITH { "analyzer": "english" }
| KEEP highlight_title
```


| highlight_title:keyword        |
|--------------------------------|
| The Lord of the <em>Rings</em> |


### Highlight multiple fields

Highlight multiple columns at once by listing them in `ON`:
```esql
ROW title = "Return of the King", body = "Tolkien wrote the epic saga."
| HIGHLIGHT "king tolkien" ON title, body
| KEEP highlight_title, highlight_body
```


| highlight_title:keyword     | highlight_body:keyword                |
|-----------------------------|---------------------------------------|
| Return of the <em>King</em> | <em>Tolkien</em> wrote the epic saga. |


### Highlight an extracted or computed field

`HIGHLIGHT` re-analyzes field values at query time, so it works on columns created earlier in the pipeline:
```esql
ROW raw = "2024 Sauron Mordor"
| DISSECT raw "%{yr} %{name} %{place}"
| HIGHLIGHT "sauron" ON name
| KEEP name, highlight_name
```


| name:keyword | highlight_name:keyword |
|--------------|------------------------|
| Sauron       | <em>Sauron</em>        |


### HTML-encode text for safe display

Use `"encoder": "html"` to escape HTML tags and special characters in the text while keeping the highlight tags intact:
```esql
ROW content = "Use <b>bold</b> tags & special chars with the Ring."
| HIGHLIGHT "ring" ON content WITH { "encoder": "html" }
| KEEP highlight_content
```


| highlight_content:keyword                              |
|--------------------------------------------------------|
| Use bboldb tags  special chars with the <em>Ring</em>. |


### Return the full text without fragmenting

Set `"number_of_fragments": 0` to return the complete text value with matches highlighted rather than returning individual snippets:
```esql
ROW content = "Elasticsearch is fast. Elasticsearch is scalable. Elasticsearch is open."
| HIGHLIGHT "elasticsearch" ON content WITH { "number_of_fragments": 0 }
| KEEP highlight_content
```


| highlight_content:keyword                                                                           |
|-----------------------------------------------------------------------------------------------------|
| <em>Elasticsearch</em> is fast. <em>Elasticsearch</em> is scalable. <em>Elasticsearch</em> is open. |


### Customize highlight tags

Use `pre_tags` and `post_tags` to specify custom wrapping tags:
```esql
ROW content = "The quick brown fox jumps over the lazy dog."
| HIGHLIGHT "fox" ON content WITH { "pre_tags": ["<b>"], "post_tags": ["</b>"] }
| KEEP highlight_content
```


| highlight_content:keyword                           |
|-----------------------------------------------------|
| The quick brown <b>fox</b> jumps over the lazy dog. |


### Customize output column names

Use `prefix` to change the column name prefix:
```esql
ROW content = "The One Ring was forged by Sauron."
| HIGHLIGHT prefix = "hl_" "ring" ON content
| KEEP content, hl_content
```


| content:keyword                    | hl_content:keyword                          |
|------------------------------------|---------------------------------------------|
| The One Ring was forged by Sauron. | The One <em>Ring</em> was forged by Sauron. |


### Overwrite the original column

Set an empty prefix (`prefix = ""`) to replace the source column with the highlighted output:
```esql
ROW content = "The quick brown fox jumps over the lazy dog."
| HIGHLIGHT prefix = "" "fox" ON content
| KEEP content
```


| content:keyword                                       |
|-------------------------------------------------------|
| The quick brown <em>fox</em> jumps over the lazy dog. |


### Return leading text when nothing matches

By default, non-matching fields evaluate to `null`. Set `no_match_size` to return text from the start of the field instead:
```esql
ROW content = "Gardens and flowers bloom in spring."
| HIGHLIGHT "elasticsearch" ON content WITH { "no_match_size": 200 }
| KEEP highlight_content
```


| highlight_content:keyword            |
|--------------------------------------|
| Gardens and flowers bloom in spring. |


### Order snippets by relevance score

Use `"order": "score"` to sort snippets by relevance score rather than document order:
```esql
ROW content = ["fast search", "fast and fast results"]
| HIGHLIGHT "fast" ON content WITH { "order": "score" }
| KEEP highlight_content
```


| highlight_content:keyword                                       |
|-----------------------------------------------------------------|
| [<em>fast</em> and <em>fast</em> results, <em>fast</em> search] |