Highlighting in Elasticsearch: Suchergebnisse gezielt hervorheben
AI generated
_doc
_index
Elasticsearch · Highlighting · Such-UX
Highlighting: Suchergebnisse gezielt hervorheben
Unified, Plain und FVH Highlighter richtig einsetzen

Eine Trefferliste ohne Highlighting zwingt Nutzer, jeden Treffer selbst nach dem gesuchten Begriff zu durchsuchen. Elasticsearch Highlighting markiert relevante Textstellen automatisch im Suchergebnis, doch die Wahl zwischen Unified, Plain und FVH Highlighter sowie die Feinjustierung von Fragment-Size entscheiden darüber, ob die Hervorhebung tatsächlich hilfreich oder nur ablenkend wirkt.

15 Min. Lesezeit highlight · Unified · Plain · FVH · Fragment Size Elasticsearch 8.x · OpenSearch 2.x

1. Warum Highlighting die Such-UX entscheidend verbessert

Eine Trefferliste, die nur Titel und Beschreibung ohne visuelle Hervorhebung zeigt, lässt Nutzer im Unklaren, warum ein Dokument überhaupt als Treffer erschienen ist. Highlighting löst dieses Problem, indem es die konkreten Textstellen markiert, die zum Match geführt haben, meist fett oder farblich hervorgehoben durch HTML-Tags wie <em>. Nutzer erfassen dadurch in Sekundenbruchteilen, ob ein Treffer tatsächlich relevant ist, ohne den gesamten Text lesen zu müssen.

Der Effekt von gutem Highlighting geht über reine Bequemlichkeit hinaus: Studien zur Suchergebnis-Wahrnehmung zeigen, dass hervorgehobene Suchbegriffe die Klickrate auf relevante Treffer signifikant erhöhen und gleichzeitig die Absprungrate bei irrelevanten Treffern senken, weil Nutzer diese schneller als nicht passend erkennen. Elasticsearch bietet dafür mehrere Highlighter-Implementierungen mit unterschiedlichen Stärken, und die richtige Wahl hängt stark vom Mapping der betroffenen Felder ab.

Wichtig ist von Anfang an: Highlighting ist kein kostenloser Zusatz zur Suche, sondern ein eigener Verarbeitungsschritt, der Rechenzeit kostet. Wer versteht, wie die einzelnen Highlighter arbeiten und wie sich Fragment-Parameter auf Performance und Darstellung auswirken, kann Highlighting gezielt einsetzen, ohne die Suchgeschwindigkeit unnötig zu belasten.

2. Der highlight Parameter: Grundstruktur

Highlighting wird über den highlight Parameter direkt im _search Request aktiviert, auf derselben Ebene wie query. Für jedes Feld, das Highlighting erhalten soll, wird ein eigener Eintrag unter fields definiert. Die Antwort enthält für jeden Treffer ein zusätzliches highlight-Objekt mit einem Array von Textfragmenten pro Feld, in denen die Treffer-Begriffe bereits mit den konfigurierten Tags umschlossen sind.

Standardmäßig verwendet Elasticsearch die Tags <em> und </em>, was sich über die Parameter pre_tags und post_tags beliebig anpassen lässt. Wichtig ist, dass Highlighting nur auf Feldern funktioniert, die als text gemappt sind, nicht auf keyword-Feldern ohne Analyse, da die Wortgrenzen-Erkennung auf der Tokenisierung des Analyzers basiert.


GET /produkte/_search
{
  "query": {
    "match": { "beschreibung": "wasserdichte wanderschuhe" }
  },
  "highlight": {
    "fields": {
      "beschreibung": {}
    }
  }
}

# Antwort (Ausschnitt):
# "hits": [
#   {
#     "_source": { "beschreibung": "Diese wasserdichte Wanderschuhe eignen sich fuer..." },
#     "highlight": {
#       "beschreibung": [
#         "Diese <em>wasserdichte</em> <em>Wanderschuhe</em> eignen sich fuer..."
#       ]
#     }
#   }
# ]

Ein häufiger Anfängerfehler: Highlighting-Konfiguration wird auf ein Feld angewendet, das im Mapping als keyword definiert ist. In diesem Fall bleibt das highlight-Objekt in der Antwort entweder leer oder markiert den kompletten Feldwert, weil keine sinnvolle Tokenisierung stattgefunden hat. Für Highlighting eignen sich ausschließlich analysierte text-Felder oder deren Multi-Field-Varianten.

3. Die drei Highlighter im Vergleich: Unified, Plain, FVH

Elasticsearch bietet drei Highlighter-Implementierungen, die über den Parameter type gewählt werden. Der Unified Highlighter ist seit Elasticsearch 6 der Standard und arbeitet direkt mit Lucene-Offsets, was ihn effizient und für die meisten Anwendungsfälle geeignet macht. Er unterstützt sowohl einfache Term-Queries als auch komplexe Bool- und Phrase-Queries ohne Zusatzkonfiguration.

Der Plain Highlighter ist die älteste Implementierung und analysiert Text zur Laufzeit erneut, was ihn bei großen Dokumenten spürbar langsamer macht als die Alternativen. Sein Vorteil liegt in der granularen Kontrolle über Highlighting-Verhalten bei komplexen Query-Strukturen, weshalb er gelegentlich noch für Spezialfälle mit sehr spezifischen Highlighting-Anforderungen eingesetzt wird. Der FVH (Fast Vector Highlighter) benötigt aktivierte term_vector: with_positions_offsets im Mapping, ist dafür aber besonders performant bei großen Textfeldern und unterstützt Multi-Term-Highlighting mit individueller Gewichtung der Treffer-Reihenfolge über boundary_scanner.


PUT /artikel
{
  "mappings": {
    "properties": {
      "inhalt": {
        "type": "text",
        "term_vector": "with_positions_offsets"
      }
    }
  }
}

GET /artikel/_search
{
  "query": { "match": { "inhalt": "elasticsearch performance" } },
  "highlight": {
    "type": "fvh",
    "fields": {
      "inhalt": {
        "number_of_fragments": 3,
        "fragment_size": 150
      }
    }
  }
}

Die Faustregel in der Praxis: Für die meisten Anwendungen ist der Unified Highlighter die richtige Wahl, weil er ohne Mapping-Änderungen funktioniert und eine gute Balance aus Performance und Funktionsumfang bietet. FVH lohnt sich bei sehr großen Textfeldern mit hoher Suchfrequenz, wo der zusätzliche Speicherbedarf für Term Vectors durch die bessere Highlighting-Performance gerechtfertigt ist.

4. Fragment-Size und Number-of-Fragments feinjustieren

Bei langen Textfeldern wird nicht der gesamte Text zurückgegeben, sondern kurze Ausschnitte, sogenannte Fragmente, die den Treffer im Kontext zeigen. Der Parameter fragment_size steuert die maximale Zeichenlänge eines Fragments, standardmäßig 100 Zeichen, während number_of_fragments festlegt, wie viele solcher Fragmente maximal zurückgegeben werden, standardmäßig fünf. Diese beiden Parameter zusammen bestimmen maßgeblich, wie viel Kontext der Nutzer um einen Treffer herum sieht.

Eine zu kleine fragment_size zerschneidet Sätze mitten im Wort oder liefert Fragmente ohne verständlichen Kontext, während eine zu große fragment_size die Antwortgröße unnötig aufbläht und die eigentliche Trefferstelle im Fließtext untergehen lässt. Für Produktbeschreibungen mit kurzen, prägnanten Sätzen sind 100 bis 150 Zeichen meist ausreichend, während redaktionelle Artikel mit komplexen Satzstrukturen von 200 bis 250 Zeichen profitieren, um vollständige Sätze abzubilden.


GET /artikel/_search
{
  "query": { "match": { "inhalt": "cluster resharding" } },
  "highlight": {
    "fields": {
      "inhalt": {
        "fragment_size": 180,
        "number_of_fragments": 2,
        "no_match_size": 150,
        "order": "score"
      }
    }
  }
}

Der Parameter order: score sortiert die zurückgegebenen Fragmente nach ihrer Relevanz statt nach ihrer Position im Dokument, was besonders bei langen Texten mit mehreren Treffer-Clustern sinnvoll ist. Der Parameter no_match_size liefert einen Standard-Ausschnitt vom Dokumentanfang, falls kein Highlighting-Treffer im Feld gefunden wurde, was verhindert, dass ein Treffer in der Anzeige komplett ohne Vorschautext dasteht.

5. Highlighting auf Multi-Fields und verschachtelten Feldern

Viele Mappings definieren Multi-Fields, etwa ein Feld titel vom Typ text mit einem Sub-Feld titel.raw vom Typ keyword für exaktes Sortieren und Filtern. Highlighting funktioniert ausschließlich auf dem analysierten text-Teil, niemals auf dem keyword-Sub-Feld. Wird versehentlich titel.raw statt titel im highlight-Parameter angegeben, bleibt das Ergebnis leer, obwohl die Suche selbst erfolgreich war.

Bei verschachtelten Objekten (nested Type) ist zusätzliche Sorgfalt nötig: Highlighting auf Feldern innerhalb eines nested Objekts erfordert entweder eine nested Query mit korrekt referenziertem Pfad oder die explizite Angabe des options.inner_hits Parameters, damit Elasticsearch weiß, in welchem verschachtelten Dokument nach dem Treffer gesucht werden soll. Ohne diese Zuordnung kann Highlighting auf nested Feldern schlicht keine Ergebnisse liefern, selbst wenn die Query korrekt matcht.


PUT /katalog
{
  "mappings": {
    "properties": {
      "titel": {
        "type": "text",
        "fields": { "raw": { "type": "keyword" } }
      },
      "bewertungen": {
        "type": "nested",
        "properties": {
          "kommentar": { "type": "text" }
        }
      }
    }
  }
}

GET /katalog/_search
{
  "query": {
    "nested": {
      "path": "bewertungen",
      "query": { "match": { "bewertungen.kommentar": "top qualitaet" } },
      "inner_hits": {
        "highlight": {
          "fields": { "bewertungen.kommentar": {} }
        }
      }
    }
  }
}

6. Pre/Post-Tags und benutzerdefinierte Hervorhebung

Für individuelles Styling im Frontend werden die Standard-Tags <em> und </em> häufig durch eigene Markup-Elemente ersetzt, etwa <mark class="hit">, um über CSS-Klassen ein spezifisches Farbschema anzuwenden. Der Parameter pre_tags und post_tags akzeptiert dabei Arrays, was bei mehreren unterschiedlichen Suchbegriffen eine gestaffelte Farbcodierung ermöglicht: Der erste Begriff bekommt das erste Tag-Paar, der zweite Begriff das zweite und so weiter.

Eine besonders elegante Option ist "tags_schema": "styled", ein vordefiniertes Set aus zehn unterschiedlichen CSS-Klassen (hlt1 bis hlt10), das automatisch für verschiedene Suchbegriffe unterschiedliche Klassen vergibt, ohne dass die Tag-Arrays manuell definiert werden müssen. Diese Option eignet sich gut, wenn mehrere Suchbegriffe gleichzeitig hervorgehoben werden sollen und visuell unterscheidbar sein müssen.


GET /produkte/_search
{
  "query": {
    "multi_match": {
      "query": "wasserdicht atmungsaktiv",
      "fields": ["beschreibung"]
    }
  },
  "highlight": {
    "pre_tags": ["<mark class=\"hit-a\">", "<mark class=\"hit-b\">"],
    "post_tags": ["</mark>", "</mark>"],
    "fields": {
      "beschreibung": {}
    }
  }
}

7. Highlighting bei Phrasen- und Fuzzy-Queries

Bei einer match_phrase Query hebt Highlighting standardmäßig nur die exakte Wortfolge hervor, die dem Phrasen-Match entspricht, nicht einzelne Wörter außerhalb der Phrase. Das ist bei Zitat- oder Titel-Suche meist das gewünschte Verhalten, weil es dem Nutzer klar signalisiert, welche exakte Formulierung getroffen hat. Bei einer Fuzzy-Query hingegen, die auch leicht abweichende Schreibweisen matcht, kann Highlighting inkonsistent wirken, weil der abweichende Term im Dokument nicht exakt dem Suchbegriff entspricht.

Für konsistentes Highlighting bei Fuzzy-Queries empfiehlt sich der Unified Highlighter mit aktivierter Option "highlight_query", bei der eine separate, meist präzisere Query nur für das Highlighting definiert wird, unabhängig von der eigentlichen Such-Query. Das erlaubt, eine breite, fehlertolerante Query für die Trefferermittlung zu nutzen, während das Highlighting auf Basis einer engeren Query erfolgt und damit präziser wirkt.


GET /produkte/_search
{
  "query": {
    "fuzzy": { "titel": { "value": "wanderschue", "fuzziness": "AUTO" } }
  },
  "highlight": {
    "fields": {
      "titel": {
        "highlight_query": {
          "match": { "titel": "wanderschuhe" }
        }
      }
    }
  }
}

8. Performance-Kosten von Highlighting bei großen Dokumenten

Highlighting ist kein triviales Feature ohne Kosten. Der Plain Highlighter analysiert den betroffenen Text bei jeder Anfrage neu, was bei großen Feldern mit mehreren Kilobyte Text pro Dokument zu spürbaren Latenzzuwächsen führen kann, besonders wenn viele Treffer gleichzeitig Highlighting benötigen. Der Unified Highlighter ist hier effizienter, weil er Lucene-interne Offsets nutzt, verursacht aber bei sehr großen Feldern ebenfalls messbaren Overhead gegenüber einer Suche ohne Highlighting.

Der FVH bietet die beste Performance bei großen Feldern, erkauft sich das aber durch zusätzlichen Speicherbedarf im Index, weil Term Vectors mit Positions- und Offset-Informationen für jedes Dokument gespeichert werden müssen. Diese Index-Größe kann bei umfangreichen Textfeldern spürbar wachsen. Eine bewährte Praxis ist, Highlighting nur auf den tatsächlich in der UI angezeigten Feldern zu aktivieren, statt pauschal auf alle durchsuchbaren Felder, und die size-Begrenzung der Suchergebnisse konsequent niedrig zu halten, da Highlighting nur für die zurückgegebenen Treffer berechnet wird.

Highlighter Mapping-Anforderung Performance Typischer Einsatz
Unified Keine, funktioniert out-of-the-box Gut, Standardwahl Die meisten Anwendungsfaelle
Plain Keine Langsam bei grossen Feldern Sehr spezielle Highlighting-Logik
FVH term_vector: with_positions_offsets Sehr gut bei grossen Feldern Grosse Textfelder, hohe Suchfrequenz

Wer unsicher ist, sollte mit dem Unified Highlighter starten und erst bei nachweisbaren Performance-Problemen auf FVH umsteigen, da der zusätzliche Mapping-Aufwand und Speicherbedarf für FVH nur bei tatsächlichem Bedarf gerechtfertigt ist.

9. Highlighting im Frontend korrekt rendern (XSS-Sicherheit)

Highlighting liefert HTML-Fragmente mit eingebetteten Tags, die im Frontend meist über dangerouslySetInnerHTML in React, v-html in Vue oder x-html in Alpine.js gerendert werden. Genau hier lauert eine ernste Sicherheitslücke: Der ursprüngliche Dokumenttext, der in das Highlighting-Fragment eingebettet wird, stammt aus Nutzereingaben oder Content-Management-Systemen und kann selbst HTML- oder Script-Inhalte enthalten, die beim direkten Rendern zu Cross-Site-Scripting führen.

Die sichere Vorgehensweise ist zweistufig: Zuerst wird der komplette Fragment-Text, inklusive der von Elasticsearch eingefügten Highlight-Tags, durch eine dedizierte HTML-Escaping-Funktion geschickt, die alle Zeichen außer den bekannten, kontrolliert eingefügten Highlight-Tags escaped. Anschließend werden ausschließlich die bekannten Highlight-Tags, etwa <mark>, gezielt wieder als echtes HTML interpretiert, entweder über einen regulären Ausdruck nach dem Escaping oder über eine dedizierte Sanitizing-Bibliothek mit einer expliziten Allowlist für erlaubte Tags.


// Beispielhafte Sanitizing-Strategie im Frontend (Pseudocode)
// 1. Rohen Fragment-Text vollstaendig HTML-escapen
// 2. Nur die bekannten Highlight-Tags gezielt wieder freigeben

function renderHighlight(fragment) {
  const escaped = escapeHtml(fragment); // escaped ALLE Tags, inkl. <mark>
  // Kontrolliertes Zurueckwandeln NUR der erwarteten Highlight-Tags:
  return escaped
    .replace(/<mark>/g, "<mark>")
    .replace(/<\/mark>/g, "</mark>");
}

// Alternative: dedizierte Sanitizing-Bibliothek mit Allowlist ["mark"]

Eine Allowlist-basierte Sanitizing-Bibliothek ist grundsätzlich robuster als eine manuelle Regex-Lösung, weil sie auch Edge Cases wie verschachtelte oder fehlerhaft geschlossene Tags korrekt behandelt. Der wichtigste Grundsatz bleibt: Highlighting-Fragmente niemals ungeprüft direkt als HTML rendern, unabhängig davon, wie vertrauenswürdig die Datenquelle erscheint, denn Elasticsearch selbst führt kein HTML-Escaping des ursprünglichen Dokumentinhalts durch.

Mironsoft

Elasticsearch-Suche, Highlighting und Frontend-Integration

Suchergebnisse, die niemand richtig einordnen kann?

Wir konfigurieren Highlighting fuer eure Elasticsearch-Suche, waehlen den passenden Highlighter fuer euer Datenvolumen und integrieren sicheres Rendering im Frontend.

Highlighter-Wahl

Unified, Plain oder FVH passend zu Datenvolumen und Query-Last auswaehlen

Fragment-Tuning

Fragment-Size und Anzahl auf euren Content-Typ abstimmen

Sicheres Frontend

XSS-sichere Darstellung von Highlighting-Fragmenten implementieren

10. Zusammenfassung

Highlighting verwandelt eine reine Trefferliste in eine nachvollziehbare, scanbare Suchergebnisdarstellung, indem relevante Textstellen direkt sichtbar markiert werden. Der highlight Parameter funktioniert nur auf analysierten text-Feldern, nicht auf keyword-Feldern. Die Wahl zwischen Unified, Plain und FVH Highlighter hängt von Datenvolumen und Suchfrequenz ab, wobei der Unified Highlighter für die meisten Fälle die richtige Standardwahl ist. Fragment-Size und Number-of-Fragments bestimmen, wie viel Kontext ein Nutzer um einen Treffer sieht, und sollten an den jeweiligen Content-Typ angepasst werden.

Multi-Fields und nested Objekte erfordern besondere Sorgfalt, da Highlighting nur auf dem tatsächlich analysierten Feld funktioniert. Pre- und Post-Tags erlauben individuelles Styling, während highlight_query präzises Highlighting auch bei fehlertoleranten Fuzzy-Queries ermöglicht. Im Frontend ist konsequentes HTML-Escaping mit gezielter Freigabe der bekannten Highlight-Tags unverzichtbar, um XSS-Lücken zu vermeiden.

Highlighting in Elasticsearch, das Wichtigste auf einen Blick

Highlighter-Wahl

Unified Highlighter als Standard, FVH bei grossen Feldern mit hoher Suchfrequenz.

Fragment-Tuning

fragment_size und number_of_fragments an Content-Typ anpassen, order:score fuer Relevanz.

Feldwahl

Nur auf analysierten text-Feldern moeglich, niemals auf keyword-Sub-Feldern.

Frontend-Sicherheit

Fragmente escapen, nur bekannte Highlight-Tags gezielt freigeben, XSS konsequent vermeiden.

11. FAQ: Highlighting in Elasticsearch

1Welcher Highlighter als Standard?
Unified Highlighter, funktioniert ohne Mapping-Aenderungen mit guter Balance aus Performance und Funktionsumfang.
2Highlighting auf keyword-Feld?
Funktioniert nicht, keyword-Felder sind nicht tokenisiert, es fehlen sinnvolle Wortgrenzen.
3Wann FVH statt Unified?
Bei grossen Feldern mit hoher Suchfrequenz, wenn der zusaetzliche Speicherbedarf sich lohnt.
4fragment_size und number_of_fragments?
Steuern maximale Laenge und Anzahl der zurueckgegebenen Textausschnitte pro Feld.
5Highlighting bei nested Objekten?
Erfordert nested Query mit inner_hits und eigener highlight-Konfiguration.
6Verschiedene Begriffe farblich trennen?
Ueber Tag-Arrays bei pre_tags/post_tags oder tags_schema: styled mit zehn Klassen.
7Highlighting bei Fuzzy-Queries?
highlight_query erlaubt eine separate, praezisere Query nur fuer die Hervorhebung.
8Kosten bei grossen Dokumenten?
Plain ist am langsamsten, Unified effizienter, FVH am schnellsten mit hoeherem Indexspeicher.
9Ist Highlighting automatisch XSS-sicher?
Nein, das Frontend muss Fragmente escapen und nur bekannte Highlight-Tags gezielt freigeben.
10Was macht no_match_size?
Liefert Standard-Vorschautext, falls kein Highlighting-Treffer im Feld gefunden wurde.