warum das Attribut-Mapping über die Facetten-Performance entscheidet
Faceted Navigation lebt von Terms-Aggregationen auf Produktattributen, aber diese Aggregationen sind nur so schnell wie das darunterliegende Mapping. Wer Attribute als analysierten text-Typ statt als keyword indexiert, bekommt zerstückelte Filterwerte, doppelte Facetten und Aggregationen, die bei jeder Kategorieseite unnötig viel Speicher und Rechenzeit verbrauchen.
Inhaltsverzeichnis
- 1. Warum Mapping die eigentliche Facetten-Stellschraube ist
- 2. keyword statt text: die Grundregel für Facetten-Felder
- 3. doc_values und der Speicherunterbau von Terms-Aggregationen
- 4. Global Ordinals und eager_global_ordinals
- 5. Multi-Fields: Suche und Facette im selben Attribut
- 6. Numerische Attribute: Range-Facetten statt Terms
- 7. Filter-Aggregationen für Facetten mit aktivem Filter
- 8. Kardinalität und Bucket-Größe im Griff behalten
- 9. Typische Fehler und Debugging
- 10. Zusammenfassung
- 11. FAQ
1. Warum Mapping die eigentliche Facetten-Stellschraube ist
Faceted Navigation, im Shop-Kontext oft Layered Navigation genannt, zeigt Kunden neben der Produktliste eine Reihe von Filtern mit Trefferzahlen: Marke, Farbe, Preisspanne, Material. Technisch entsteht jede dieser Facetten aus einer Terms- oder Range-Aggregation, die parallel zur eigentlichen Produktsuche über dieselbe Query läuft. Die Qualität dieser Faceted Navigation hängt aber nicht in erster Linie von der Aggregations-Syntax ab, sondern vom Mapping der zugrunde liegenden Attribute.
Ein falsch gewähltes Feld-Mapping macht sich nicht sofort bemerkbar. Die Query läuft, die Facette erscheint, aber die Werte sind zerstückelt: "Nike Air Max" wird bei einem analysierten text-Feld zu den zwei separaten Facetten-Werten "nike" und "air" und "max", statt als ein einziger Marken-Wert zu erscheinen. Dieses Muster ist der häufigste Grund, warum eine Faceted Navigation in der Praxis unbrauchbare Filterlisten produziert, obwohl die Aggregations-Query selbst syntaktisch korrekt ist.
Die folgenden Abschnitte zeigen, wie Mapping-Design und Aggregationen für eine performante Faceted Navigation zusammenspielen: von der Grundregel keyword statt text über Global Ordinals bis zu Range-Facetten für numerische Attribute wie den Preis. Jedes Beispiel nutzt reale Mapping- und Aggregations-Syntax aus dem E-Commerce-Kontext.
2. keyword statt text: die Grundregel für Facetten-Felder
Der wichtigste Mapping-Grundsatz für Faceted Navigation lautet: Jedes Attribut, das als Filter angeboten werden soll, muss als keyword gemappt sein, nicht als text. Ein text-Feld durchläuft beim Indexieren einen Analyzer, der den Wert in einzelne Tokens zerlegt, kleinschreibt und gegebenenfalls Stopwörter entfernt. Eine Terms-Aggregation auf einem solchen Feld bildet Facetten aus diesen Tokens, nicht aus dem ursprünglichen Attributwert, was bei mehrwortigen Markennamen oder Kategoriebezeichnungen zu granularen, für Kunden unbrauchbaren Filtern führt.
Ein keyword-Feld dagegen wird unverändert und ohne Analyse indexiert. "Nike Air Max" bleibt exakt "Nike Air Max" als ein einziger Aggregations-Bucket. Für Farbwerte, Größenangaben, Markennamen und ähnliche kategoriale Attribute ist keyword deshalb praktisch immer die richtige Wahl für das Facetten-Mapping, unabhängig davon, ob dasselbe Attribut zusätzlich in der Volltextsuche eine Rolle spielt.
PUT /products
{
"mappings": {
"properties": {
"brand": { "type": "keyword" },
"color": { "type": "keyword" },
"size": { "type": "keyword" },
"material": { "type": "keyword" },
"price": { "type": "scaled_float", "scaling_factor": 100 },
"name": {
"type": "text",
"analyzer": "standard"
}
}
}
}
3. doc_values und der Speicherunterbau von Terms-Aggregationen
Terms-Aggregationen greifen nicht auf den invertierten Index zu, sondern auf eine separate spaltenorientierte Datenstruktur namens doc_values. Diese Struktur wird für jedes indexierte Feld standardmäßig automatisch mitgeschrieben, außer sie wird explizit deaktiviert, und ist so organisiert, dass Elasticsearch für eine Aggregation direkt "für jedes Dokument, welchen Wert hat Feld X" abfragen kann, statt wie bei einer Textsuche "welche Dokumente enthalten Wert Y". Diese spaltenorientierte Organisation ist der eigentliche Grund, warum Terms-Aggregationen auf keyword-Feldern für Faceted Navigation so effizient sind.
Ein wichtiges Detail für das Mapping-Design: doc_values für ein Feld zu deaktivieren spart etwas Speicher auf der Platte, macht aber jede Aggregation auf diesem Feld unmöglich. Für Attribute, die ausschließlich für die Volltextsuche gebraucht werden und niemals als Facette dienen sollen, kann das eine sinnvolle Optimierung sein. Für jedes Attribut, das Teil der Faceted Navigation ist, müssen doc_values dagegen zwingend aktiv bleiben, was standardmäßig ohnehin der Fall ist.
4. Global Ordinals und eager_global_ordinals
Bei keyword-Feldern nutzt Elasticsearch für Terms-Aggregationen eine Optimierung namens Global Ordinals: Statt bei jeder Aggregation die vollständigen Textwerte zu vergleichen, wird jedem eindeutigen Wert im Segment eine numerische ID zugewiesen, und die Aggregation rechnet nur noch mit diesen IDs. Der Aufbau dieser Ordinal-Struktur passiert standardmäßig beim ersten Aggregations-Request nach einem Segment-Merge oder Refresh, was diese erste Anfrage spürbar langsamer macht als alle folgenden.
Für Faceted Navigation auf stark frequentierten Kategorieseiten ist dieser einmalige Aufbau-Overhead unerwünscht, weil er zufällig einzelne Nutzer mit langsamen Antwortzeiten trifft, sobald ein Refresh die Ordinals invalidiert hat. Der Parameter eager_global_ordinals im Mapping löst dieses Problem, indem er den Aufbau der Ordinal-Struktur direkt nach jedem Refresh im Hintergrund anstößt, statt auf die nächste Nutzeranfrage zu warten. Diese Einstellung kostet etwas zusätzliche Indexierungszeit, verschiebt den Aufbau-Overhead aber komplett aus dem Nutzerpfad heraus.
PUT /products/_mapping
{
"properties": {
"brand": {
"type": "keyword",
"eager_global_ordinals": true
},
"color": {
"type": "keyword",
"eager_global_ordinals": true
}
}
}
5. Multi-Fields: Suche und Facette im selben Attribut
Viele Attribute sollen gleichzeitig durchsuchbar und facettierbar sein, etwa der Produktname oder die Kategorie. Elasticsearch löst diesen Konflikt mit Multi-Fields: Das Hauptfeld wird als text für die Volltextsuche gemappt, während ein Sub-Feld unter dem Namen .keyword denselben Wert zusätzlich unanalysiert speichert. Aggregationen für Faceted Navigation greifen dann gezielt auf das Sub-Feld zu, während die Suche weiterhin über das analysierte Hauptfeld läuft.
Dieses Muster vermeidet doppelte Datenhaltung im Quell-Dokument, weil Elasticsearch beide Repräsentationen automatisch aus demselben Eingabewert ableitet. Wichtig ist, im Aggregations-Request konsequent den vollen Pfad mit .keyword-Suffix zu verwenden, sonst greift die Aggregation versehentlich auf das analysierte Textfeld zu und produziert wieder zerstückelte Facetten-Werte.
PUT /products/_mapping
{
"properties": {
"category_name": {
"type": "text",
"fields": {
"keyword": { "type": "keyword" }
}
}
}
}
GET /products/_search
{
"size": 0,
"aggs": {
"categories": {
"terms": { "field": "category_name.keyword", "size": 20 }
}
}
}
6. Numerische Attribute: Range-Facetten statt Terms
Für den Preis oder ähnliche kontinuierliche numerische Attribute ist eine Terms-Aggregation ungeeignet, weil jeder einzelne Preis potenziell einen eigenen Bucket bildet und daraus keine sinnvolle Filterliste entsteht. Für diese Fälle ist die range-Aggregation die richtige Wahl innerhalb einer Faceted Navigation, weil sie feste oder dynamisch berechnete Preisspannen als Buckets definiert, etwa "0 bis 50 Euro", "50 bis 100 Euro" und "über 100 Euro".
Das Mapping numerischer Facetten-Felder profitiert vom scaled_float-Typ statt float, weil scaled_float intern als Integer mit einem festen Skalierungsfaktor gespeichert wird und dadurch kompakter ist und weniger Rundungsprobleme bei Aggregationen erzeugt als binäre Gleitkommazahlen. Für Preisfelder ist ein scaling_factor von 100 üblich, um Centbeträge exakt abzubilden.
GET /products/_search
{
"size": 0,
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50 },
{ "from": 50, "to": 100 },
{ "from": 100, "to": 250 },
{ "from": 250 }
]
}
}
}
}
7. Filter-Aggregationen für Facetten mit aktivem Filter
Ein häufiges Anforderungsmuster in der Faceted Navigation: Wenn ein Kunde bereits nach Farbe "Rot" filtert, sollen die anderen Facetten wie Marke weiterhin alle verfügbaren Optionen zeigen, während die Farb-Facette selbst weiterhin alle Farben inklusive Trefferzahl anzeigt, nicht nur "Rot". Diese Anforderung löst man mit einer post_filter-Kombination oder mit einer eigenen filter-Aggregation pro Facette, die den aktiven Filter dieser einen Facette gezielt ausnimmt, während alle anderen aktiven Filter weiterhin angewendet werden.
In der Praxis bewährt sich für diesen Fall eine Struktur mit einer globalen Aggregation als Wurzel, unter der für jede Facette eine filter-Aggregation alle Filter außer dem eigenen anwendet, gefolgt von der eigentlichen Terms- oder Range-Aggregation. Dieses Muster erlaubt in einer einzigen Query korrekte Multi-Select-Facetten, ohne für jede Facette eine separate Anfrage an den Cluster zu senden.
| Attributtyp | Falsches Mapping | Empfohlenes Mapping | Effekt |
|---|---|---|---|
| Marke, Farbe, Größe | text mit Analyzer | keyword | Kein Zerbrechen mehrwortiger Werte |
| Suchbarer Name | nur text, keine Facette möglich | text mit .keyword Multi-Field | Suche und Facette im selben Attribut |
| Preis | Terms-Aggregation auf float | range-Aggregation auf scaled_float | Sinnvolle Preisspannen statt Einzelwerte |
| Vielgenutzte Facette | Standard-Ordinals, verzögerter Aufbau | eager_global_ordinals: true | Aufbau-Overhead aus dem Nutzerpfad entfernt |
| Nur-Text-Attribut | doc_values standardmäßig aktiv, kostet Speicher | doc_values: false, falls keine Facette nötig | Speicher sparen, wenn nie aggregiert wird |
8. Kardinalität und Bucket-Größe im Griff behalten
Die size-Angabe einer Terms-Aggregation bestimmt, wie viele Facetten-Werte zurückkommen, hat aber direkten Einfluss auf die Genauigkeit bei verteilten Indizes mit mehreren Shards. Da jeder Shard seine Top-N-Werte unabhängig ermittelt und der koordinierende Knoten diese Teilergebnisse zusammenführt, können bei knapper size-Angabe Werte mit tatsächlich hoher Gesamthäufigkeit fälschlich fehlen, wenn sie auf einzelnen Shards nicht unter den lokalen Top-N lagen. Für kritische Faceted Navigation-Facetten wie Marke ist deshalb eine großzügigere size in Kombination mit dem Parameter shard_size sinnvoll, der explizit steuert, wie viele Kandidaten jeder Shard vor dem Merge liefert.
Attribute mit sehr hoher Kardinalität, etwa individuelle Artikelnummern oder Freitext-Tags, eignen sich grundsätzlich schlecht als Facette, weil eine Terms-Aggregation mit tausenden Buckets weder für Nutzer sinnvoll noch für den Cluster performant ist. Für solche Felder ist entweder eine Vorab-Kategorisierung in gröbere Gruppen oder der komplette Verzicht auf eine Facette die bessere Wahl, unabhängig vom gewählten Mapping.
9. Typische Fehler und Debugging
Der häufigste Fehler bei Faceted Navigation ist eine Terms-Aggregation direkt auf ein analysiertes text-Feld ohne .keyword-Suffix. Das Ergebnis sind Facetten-Werte, die wie einzelne Wörter aussehen statt wie vollständige Attributwerte, oft bereits an der Kleinschreibung erkennbar, da der Standard-Analyzer alle Werte lowercased. Ein zweiter häufiger Fehler ist ein fehlendes Reindex nach einer Mapping-Änderung: Ein bestehendes Feld nachträglich von text auf keyword umzustellen, wirkt sich nur auf neu indexierte Dokumente aus, bereits vorhandene Dokumente behalten ihr altes Mapping bis zum vollständigen Reindex.
// WRONG: aggregating directly on an analyzed text field
{
"aggs": {
"brands": { "terms": { "field": "brand_name" } }
}
}
// Result: fragmented tokens like "nike", "air", "max" instead of one value
// RIGHT: aggregate on the keyword sub-field
{
"aggs": {
"brands": { "terms": { "field": "brand_name.keyword" } }
}
}
// WRONG: mapping change without reindex, old documents keep old mapping
PUT /products/_mapping
{ "properties": { "brand_name": { "type": "keyword" } } }
// existing documents indexed before this change are unaffected
// RIGHT: mapping change followed by a full reindex
POST /_reindex
{
"source": { "index": "products" },
"dest": { "index": "products_v2" }
}
Mironsoft
Elasticsearch- und OpenSearch-Beratung für Suche, Analytics und Dashboards
Facettennavigation, die auch bei vielen Attributen performant bleibt?
Wir gestalten Attribut-Mapping und Aggregationen so, dass Faceted Navigation korrekte Filterwerte, saubere Trefferzahlen und schnelle Antwortzeiten liefert, von der Mapping-Analyse bis zur Multi-Select-Facette.
Mapping-Review
Prüfung bestehender Attribut-Mappings auf keyword- und Multi-Field-Fehler
Facetten-Umsetzung
Terms- und Range-Aggregationen für Layered Navigation im Shop
Performance-Tuning
eager_global_ordinals, shard_size und Reindex-Strategie für große Kataloge
10. Zusammenfassung
Eine performante Faceted Navigation entsteht nicht durch die Aggregations-Query allein, sondern durch das darunterliegende Mapping. keyword statt text ist die Grundregel für jedes filterbare Attribut, Multi-Fields lösen den Konflikt zwischen Suche und Facette im selben Feld, und range-Aggregationen auf scaled_float ersetzen Terms-Aggregationen für kontinuierliche numerische Werte wie den Preis.
eager_global_ordinals verschiebt den Aufbau-Overhead von Global Ordinals aus dem Nutzerpfad, shard_size sichert die Genauigkeit bei verteilten Indizes ab, und filter-Aggregationen ermöglichen korrekte Multi-Select-Facetten in einer einzigen Query. Wer diese Bausteine im Mapping von Anfang an berücksichtigt, spart sich spätere Reindex-Zyklen und liefert Kunden eine Facettennavigation mit korrekten, unzerstückelten Filterwerten.
Faceted Navigation und Mapping-Design, das Wichtigste auf einen Blick
Grundregel keyword
Jedes Filter-Attribut als keyword mappen, niemals als analysierten text-Typ, sonst zerbrechen Werte in Tokens.
Multi-Fields
.keyword Sub-Feld für Facetten, text-Hauptfeld für Volltextsuche, beide aus demselben Wert abgeleitet.
Numerische Facetten
range-Aggregation auf scaled_float statt Terms-Aggregation für Preis und ähnliche kontinuierliche Werte.
Performance
eager_global_ordinals für vielgenutzte Facetten, shard_size für Genauigkeit, doc_values gezielt deaktivieren.