Highlighting

This section documents the current Solr line (10.0; 9.10.x the maintained 9.x branch), written and verified against the Apache Solr Reference Guide. No specific patch version is pinned. Some capabilities (the Solr Operator on Kubernetes, the package-manager ecosystem, Learning To Rank model training, and expert plugin development) are linked, not documented in depth.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

A search results page rarely shows a stored field verbatim — it shows a short snippet around the matched terms, with those terms wrapped in a tag the UI can style. Solr’s highlighting component produces that snippet per document per field, driven entirely by hl.* request parameters layered on top of an ordinary query. This page covers the three highlighter implementations, the parameters that control them, per-field overrides, phrase and multivalued-field behavior, and the index-time offset source each highlighter reads from.

Turning highlighting on

Add hl=true and hl.fl (the fields to highlight) to any query. The response gains a top-level highlighting section keyed by document id, each holding a list of snippets per requested field.

curl "http://localhost:8983/solr/books/select?q=title:distributed&hl=true&hl.fl=title&hl.snippets=2&hl.fragsize=100"

A response looks like:

{
  "highlighting": {
    "book-42": {
      "title": ["A <em>Distributed</em> Systems Primer", "Building <em>Distributed</em> Databases"]
    }
  }
}

hl.fl accepts a comma- or space-separated list of fields, or a glob (hl.fl=_txt); it defaults to the df field if omitted. Only fields the query actually matched against produce snippets unless hl.requireFieldMatch=false is set, which highlights a field for terms that matched a *different field (common when a query targets a copyField destination).

Choosing a highlighter

Three highlighter implementations exist, selected with hl.method; each trades off accuracy, speed, and the index-time data it needs.

hl.method When to use

unified

The default since Solr 6.4. A single Lucene component that picks the fastest available offset source automatically (postings, term vectors, or on-the-fly re-analysis), handles nearly every query type including wildcards and complex boolean/phrase queries, and needs no term vectors to work well. Start here and only move off it for a specific gap.

original

The oldest highlighter (hl.method=original, formerly the only option). Re-analyzes the field’s stored value with a QueryScorer at request time. Works without any term-vector configuration, but is the slowest on large fields and has weaker phrase-query accuracy than the other two.

fastVector (FVH)

hl.method=fastVector. Reads directly from term vectors with offsets, so it needs termVectors="true" termPositions="true" termOffsets="true" on the field. Fast on large fields because it never re-analyzes them, and supports multi-colored highlighting per matched clause via hl.tag.pre/hl.tag.post lists, but that term-vector requirement enlarges the index.

curl "http://localhost:8983/solr/books/select?q=title:%22distributed+systems%22&hl=true&hl.fl=title&hl.method=unified"

The Unified Highlighter’s own offset-source preference, cheapest to most expensive, is: postings with storeOffsetsWithPositions (see below), then term vectors, then plain re-analysis — it uses whichever the field actually has, so upgrading a field’s index-time options can speed it up without changing any hl.* parameter.

storeOffsetsWithPositions and term-vector offsets

Highlighting needs to know where each matched term sits in the original text, and that data comes from one of two index-time sources declared on the field (or field type), not from hl.*:

  • storeOffsetsWithPositions="true" on the field, alongside indexed="true", stores character offsets in the postings list itself. This is what lets hl.method=unified use its fast postings offset strategy without any term-vector overhead — the recommended setup for the default highlighter on large collections.

  • Term vectors — termVectors="true" termPositions="true" termOffsets="true" — store a separate per-document, per-field list of terms with positions and offsets. The FastVector highlighter requires this; the Unified Highlighter will fall back to it when postings offsets are not available; the Original highlighter needs neither and instead re-tokenizes the stored field value, so that field must also be stored="true".

<!-- schema.xml: recommended for hl.method=unified on a large text field -->
<field name="body" type="text_general" indexed="true" stored="true"
       storeOffsetsWithPositions="true"/>

<!-- schema.xml: required for hl.method=fastVector -->
<field name="body_fvh" type="text_general" indexed="true" stored="true"
       termVectors="true" termPositions="true" termOffsets="true"/>

Both options enlarge the index on disk; storeOffsetsWithPositions is generally the cheaper of the two and is why it is favored for the Unified Highlighter. Field configuration is covered in full in Schema & fields and Field types.

Core hl.* parameters

Parameter Effect

hl.fl

Fields to highlight (comma/space-separated list or glob).

hl.snippets

Maximum number of snippets returned per field (default 1).

hl.fragsize

Target snippet length in characters (default 100; 0 returns the whole field value, unfragmented).

hl.simple.pre / hl.simple.post

Tags wrapping each matched term, default <em> / </em>. Ignored by fastVector’s multi-tag mode, which uses `hl.tag.pre/hl.tag.post instead.

hl.requireFieldMatch

When true (the default in modern Solr for unified), only highlights a field for terms that matched that field. Set false to highlight regardless of which field the query actually matched.

hl.highlightMultiTerm

Whether wildcard/prefix/fuzzy/range query terms are highlighted, not just exact term matches. Defaults to true for unified.

hl.bs.type

The BreakIterator type the Unified and Original highlighters use to find fragment boundaries: SENTENCE (default), WORD, CHARACTER, LINE, or WHOLE (never fragment).

hl.maxAnalyzedChars

How much of a field’s value is scanned when building fragments before giving up (default 51200); raise it for very large fields where the match may occur late in the text.

hl.q / hl.qparser

Highlight against a different parsed query than the one that selected the document — for example, to also mark synonyms the main query expanded but the user did not type.

curl "http://localhost:8983/solr/books/select?q=body:resilient&hl=true&hl.fl=body&hl.snippets=3&hl.fragsize=120&hl.bs.type=SENTENCE&hl.simple.pre=%3Cmark%3E&hl.simple.post=%3C%2Fmark%3E"

The full parameter reference, including the FastVector-only hl.fragListBuilder, hl.fragmentsBuilder, and hl.boundaryScanner family, is on Highlighting.

Per-field parameter overrides

Any hl. parameter can be overridden for one field with an f.<field>.hl. prefix, which takes precedence over the plain hl.* value for that field only. This is the usual way to give a short title field a single unfragmented snippet while a long body field gets several short ones.

curl "http://localhost:8983/solr/books/select?q=title:distributed+body:resilient&hl=true" \
  --data-urlencode "hl.fl=title,body" \
  --data-urlencode "f.title.hl.fragsize=0" \
  --data-urlencode "f.title.hl.snippets=1" \
  --data-urlencode "f.body.hl.fragsize=150" \
  --data-urlencode "f.body.hl.snippets=3"

Phrase and multivalued-field highlighting

The Unified and FastVector highlighters both account for term proximity: a snippet containing the exact phrase a match_phrase-style query asked for is preferred over one where the same terms appear scattered, and only the terms that actually satisfy the phrase are tagged rather than every occurrence of each individual word. The Original highlighter, lacking positional awareness by default, is more prone to highlighting a term outside its matching phrase context.

A multivalued field (an array of strings, one Solr field with several stored values) is highlighted per value: each entry that contains a match can contribute its own snippet, and hl.snippets limits how many are returned in total across all the field’s values, not per value. Where a phrase query must not match across the boundary between two values of the same multivalued field, set a positionIncrementGap greater than the maximum phrase slop on that field’s type — covered in Schema & fields — so a match spanning two array entries cannot be found (or highlighted) as though it were contiguous text.

curl "http://localhost:8983/solr/books/select?q=tags:%22machine+learning%22&hl=true&hl.fl=tags&hl.method=unified"
  • Query basics & parameters — the q, fl, and general request-parameter conventions that hl.* parameters build on.

  • Text analysis — how a field’s analyzer determines what counts as a "term" for both matching and highlighting.

  • Schema & fields and Field types — declaring storeOffsetsWithPositions, term vectors, and positionIncrementGap on a field.

  • Relevance & scoring — how the Unified Highlighter’s passage ranking relates to the query’s own scoring.

  • Spell check & suggest — the companion feature for "did you mean" and autocomplete alongside highlighted results.