Layered Navigation: die dahinterliegenden Aggregationen verstehen
AI generated
_doc
_index
Magento · Layered Navigation · Elasticsearch · Aggregationen
Layered Navigation: die dahinterliegenden
Aggregationen wirklich verstehen

Jede Filteroption in der Magento Layered Navigation, von der Markenauswahl bis zum Preis-Slider, ist das Ergebnis einer Elasticsearch-Aggregation, die im Hintergrund parallel zur eigentlichen Suchanfrage berechnet wird. Wer versteht, wie Terms- und Range-Aggregationen aufgebaut sind, kann eigene Facetten ergaenzen und Performance-Probleme in der Navigation gezielt loesen.

18 Min. Lesezeit Terms-Aggregation · Range-Aggregation · Bucket · Post-Filter Magento 2.4.x · Elasticsearch 8.x · OpenSearch 2.x

1. Was Layered Navigation technisch wirklich bedeutet

Die Layered Navigation ist die Filterleiste, die auf Kategorie- und Suchergebnisseiten in Magento erscheint: Marke, Preis, Farbe, Groesse und alle weiteren filterbaren Attribute. Was auf den ersten Blick wie eine simple Liste von Checkboxen wirkt, ist im Hintergrund das Ergebnis einer parallel zur Produktsuche ausgefuehrten Elasticsearch-Aggregation. Jede Filteroption, jede Anzahl in Klammern hinter einem Filterwert, stammt aus einem sogenannten Bucket, den Elasticsearch fuer genau diese Anfrage berechnet hat.

Der entscheidende Punkt fuer das Verstaendnis der Layered Navigation ist, dass diese Aggregationen nicht separat, sondern als Teil derselben Suchanfrage berechnet werden, die auch die Produktliste liefert. Ein einziger Request an Elasticsearch liefert sowohl die Treffer-IDs fuer die Produktliste als auch die Bucket-Daten fuer alle Filter gleichzeitig. Das ist ein wesentlicher Effizienzgewinn gegenueber einem Ansatz, der fuer jede Filteroption eine eigene Datenbankabfrage stellen wuerde, wie es die klassische MySQL-basierte Facettensuche frueher tat.

Dieser Artikel geht der Layered Navigation von der Attributkonfiguration bis zum fertigen Aggregation-Request nach: welche Aggregationstypen es gibt, wie Magento sie aus Attributdaten ableitet, wie Performance-Probleme entstehen und wie sich eigene Facetten fuer Custom-Attribute ergaenzen lassen.

2. Vom filterbaren Attribut zur Bucket-Aggregation

Nicht jedes Produktattribut erscheint automatisch in der Layered Navigation. Ausschlaggebend ist das Attribut-Flag is_filterable, das im Backend unter "Use in Layered Navigation" konfiguriert wird. Dieses Flag kennt mehrere Werte: "Filterable (with results)" zeigt nur Filteroptionen mit mindestens einem Treffer, "Filterable (no results)" zeigt auch leere Optionen an. Diese Konfiguration steuert direkt, wie Magento die spaetere Elasticsearch-Aggregation aufbaut, insbesondere den Parameter min_doc_count, der Buckets mit null Treffern aus der Antwort herausfiltert oder eben nicht.

Sobald ein Attribut als filterbar markiert ist, entscheidet sein EAV-Backend-Type und sein Frontend-Input, welcher Elasticsearch-Aggregationstyp verwendet wird. Ein Dropdown- oder Multiselect-Attribut mit diskreten Werten fuehrt zu einer Terms-Aggregation, die fuer jeden vorkommenden Wert einen Bucket mit Trefferanzahl liefert. Preis und andere numerische Attribute mit kontinuierlicher Werteverteilung fuehren dagegen zu einer Range-Aggregation, die den Wertebereich in feste oder dynamisch berechnete Intervalle unterteilt.

Diese Zuordnung geschieht in Magento ueber eine Reihe von Filter-Klassen, die je Attributtyp implementiert sind: Magento\Catalog\Model\Layer\Filter\Attribute fuer diskrete Attribute, Magento\Catalog\Model\Layer\Filter\Price fuer den Preisfilter, Magento\Catalog\Model\Layer\Filter\Category fuer die Kategorienavigation. Jede dieser Klassen kennt sowohl die Logik zum Aufbau der Aggregation-Anfrage als auch zur Umwandlung der zurueckgelieferten Buckets in anzeigbare Filteroptionen mit Label und Trefferanzahl.

3. Terms-Aggregationen fuer Dropdown und Multiselect

Die Terms-Aggregation ist der haeufigste Aggregationstyp in der Layered Navigation und kommt bei jedem Attribut mit begrenzter Wertemenge zum Einsatz, etwa Marke, Farbe oder Material. Technisch gruppiert Elasticsearch dabei alle Dokumente nach dem exakten Wert eines keyword-Feldes und liefert fuer jeden eindeutigen Wert einen Bucket mit dem Wert selbst und der Anzahl der Treffer. Wichtig: Die Terms-Aggregation arbeitet ausschliesslich auf keyword-Feldern, nicht auf text-Feldern, weil sie exakte Werte und keine Tokens zaehlt.

Der Parameter size innerhalb der Terms-Aggregation begrenzt, wie viele unterschiedliche Buckets zurueckgegeben werden. Bei Attributen mit vielen moeglichen Werten, etwa Farboptionen in einem grossen Sortiment, muss dieser Wert bewusst gesetzt werden, sonst liefert Elasticsearch standardmaessig nur die zehn haeufigsten Werte zurueck und blendet seltenere Optionen komplett aus der Navigation aus. Magento setzt diesen Wert in der Regel hoch genug, um alle relevanten Optionen abzudecken, aber bei sehr grossen Attributwertemengen lohnt sich eine explizite Pruefung.


GET /magento2_product_1_v1/_search
{
  "size": 0,
  "query": { "bool": { "filter": [ { "term": { "category_ids": 42 } } ] } },
  "aggs": {
    "brand_bucket": {
      "terms": { "field": "brand", "size": 50, "min_doc_count": 1 }
    },
    "color_bucket": {
      "terms": { "field": "color", "size": 30, "min_doc_count": 1 }
    }
  }
}

4. Range-Aggregationen fuer den Preisfilter

Der Preisfilter der Layered Navigation nutzt eine Range-Aggregation, die den kontinuierlichen Wertebereich eines numerischen Feldes in diskrete Intervalle unterteilt. Magento berechnet diese Intervalle nicht statisch, sondern dynamisch anhand der tatsaechlichen Preisverteilung der aktuell gefilterten Produkte, gesteuert ueber die Konfiguration "Price Navigation Step Calculation" mit den Optionen "Automatic (Equalize Product Counts)" und "Automatic (Equalize Price Ranges)". Die erste Variante versucht, in jedem Preisintervall eine aehnliche Anzahl an Produkten unterzubringen, die zweite teilt den Wertebereich in gleich grosse Preisspannen.

Technisch nutzt Elasticsearch fuer diesen Fall entweder eine klassische range-Aggregation mit vorab berechneten Grenzen oder eine histogram-Aggregation mit fester Intervallbreite, abhaengig von der gewaehlten Berechnungsmethode. Bei "Equalize Product Counts" muss Magento vorab die Perzentile der Preisverteilung ermitteln, bevor die eigentliche Range-Aggregation mit individuellen Grenzen abgeschickt wird, was in der Praxis zu zwei aufeinanderfolgenden Elasticsearch-Requests fuehren kann, statt zu einem einzigen kombinierten Request.


GET /magento2_product_1_v1/_search
{
  "size": 0,
  "query": { "bool": { "filter": [ { "term": { "category_ids": 42 } } ] } },
  "aggs": {
    "price_bucket": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 50 },
          { "from": 50, "to": 100 },
          { "from": 100, "to": 250 },
          { "from": 250 }
        ]
      }
    }
  }
}

5. LayerResolver und Bucket-Reader in Magento

Auf der Magento-Seite koordiniert der LayerResolver (Magento\Catalog\Model\Layer\Resolver), ob der Category-Layer oder der Search-Layer aktiv ist, je nachdem, ob eine Kategorieseite oder eine Suchergebnisseite gerendert wird. Beide Layer nutzen dieselbe zugrunde liegende Aggregationslogik, unterscheiden sich aber in den zusaetzlichen Filterbedingungen: Der Category-Layer filtert immer nach der aktuellen Kategorie-ID, der Search-Layer nach dem Volltextsuchbegriff.

Die zurueckgelieferten Buckets werden nicht direkt an das Frontend durchgereicht, sondern durch die Klasse Magento\Framework\Search\Response\Aggregation und die zugehoerigen Bucket- und Value-Objekte in ein einheitliches, engine-unabhaengiges Format uebersetzt. Diese Abstraktionsschicht ist der Grund, warum Layered-Navigation-Templates im Frontend nicht wissen muessen, ob im Hintergrund Elasticsearch oder theoretisch eine andere Such-Engine die Aggregation berechnet hat. Erst die Filter-Klassen im Layer-Modul reichern diese generischen Bucket-Daten mit Attributlabels und URL-Parametern fuer die Filterlinks an.

6. Der komplette Aggregation-Request-Body

In der Praxis kombiniert Magento fuer eine Kategorieseite mit aktivierten Filtern alle relevanten Aggregationen in einem einzigen Request, zusammen mit der eigentlichen Produktsuche. Das bedeutet, dass ein Request sowohl die query-Klausel fuer die Produktliste als auch einen kompletten aggs-Block mit allen aktiven Facetten enthaelt. Der Parameter size: 0 wird dabei bewusst nicht immer gesetzt, weil derselbe Request meist zusaetzlich die Top-N-Produkte fuer die Anzeige liefern soll, nicht nur die Aggregationsdaten.

Ein wichtiges Detail: Bereits aktivierte Filter der Layered Navigation werden fuer die Aggregation eines Attributs typischerweise ausgeklammert, waehrend sie fuer alle anderen Aggregationen weiterhin gelten. Waehlt ein Nutzer beispielsweise eine Marke, soll die Preis-Aggregation weiterhin nur Produkte dieser Marke beruecksichtigen, waehrend die Marken-Aggregation selbst alle verfuegbaren Marken innerhalb der Kategorie zeigt, nicht nur die bereits gewaehlte. Diese selektive Filterausklammerung wird ueber separate filter-Aggregationen realisiert, die um jede Terms- oder Range-Aggregation herum gelegt werden.


GET /magento2_product_1_v1/_search
{
  "size": 24,
  "query": {
    "bool": {
      "filter": [
        { "term": { "category_ids": 42 } },
        { "term": { "brand": "acme" } }
      ]
    }
  },
  "aggs": {
    "price_bucket": {
      "filter": { "bool": { "filter": [ { "term": { "category_ids": 42 } }, { "term": { "brand": "acme" } } ] } },
      "aggs": { "prices": { "range": { "field": "price", "ranges": [ { "to": 50 }, { "from": 50 } ] } } }
    },
    "brand_bucket": {
      "filter": { "bool": { "filter": [ { "term": { "category_ids": 42 } } ] } },
      "aggs": { "brands": { "terms": { "field": "brand", "size": 50 } } }
    }
  }
}

7. Performance: Aggregationen und Post-Filter

Aggregationen sind in Elasticsearch grundsaetzlich rechenintensiver als reine Filterabfragen, weil fuer jeden Bucket zusaetzliche Zaehlungen ueber die gesamte gefilterte Ergebnismenge stattfinden. Bei Kategorien mit sehr vielen Produkten und vielen gleichzeitig aktiven Facetten in der Layered Navigation kann die Anzahl paralleler Aggregationen zu spuerbar laengeren Antwortzeiten fuehren, insbesondere wenn mehrere Terms-Aggregationen mit hohem size-Wert gleichzeitig berechnet werden.

Ein bewaehrter Ansatz zur Entlastung ist, die Anzahl gleichzeitig berechneter Facetten bewusst zu begrenzen, etwa indem selten genutzte Attribute nicht standardmaessig in der Layered Navigation erscheinen, sondern erst nach Nutzerinteraktion nachgeladen werden. Ausserdem hilft doc_values, die fuer keyword- und numerische Felder standardmaessig aktiviert sind und Aggregationen erheblich beschleunigen, weil Elasticsearch dabei auf eine spaltenorientierte Datenstruktur statt auf den invertierten Index zugreift. Ein Feld mit doc_values: false zu mappen, etwa aus Speichergruenden, macht dieses Feld faktisch unbrauchbar fuer Aggregationen.

Attributtyp Aggregationstyp Feldvoraussetzung Typische Nutzung
Dropdown / Select Terms keyword-Feld Marke, Material, Status
Multiselect Terms mit Array-Feld keyword, multi-valued Farbvarianten, Tags
Preis Range oder Histogram numerisches Feld mit doc_values Preis-Slider
Kategorie Terms auf category_ids keyword-Array Unterkategorien-Navigation
Boolean Terms mit zwei Buckets boolean oder keyword Auf Lager, Neuheit

8. Custom-Attribute in die Navigation aufnehmen

Um ein neues Custom-Attribut in der Layered Navigation nutzbar zu machen, reicht die Attributkonfiguration im Backend allein nicht immer aus. Neben is_filterable muss das Attribut ueber das FieldMapper-Mapping tatsaechlich als keyword-Feld im Elasticsearch-Index landen, sonst greift die Terms-Aggregation ins Leere oder liefert unerwartete Tokens statt exakter Werte. Bei numerischen Custom-Attributen, die als Range gefiltert werden sollen, muss zusaetzlich sichergestellt sein, dass der Feldtyp tatsaechlich numerisch und nicht faelschlich als text gemappt ist.

Fuer komplexere Faelle, etwa ein Attribut mit einer eigenen Bucket-Struktur, die nicht dem Standard-Schema entspricht, bietet Magento die Moeglichkeit, eigene Filter-Klassen zu implementieren, die Magento\Catalog\Model\Layer\Filter\FilterInterface erfuellen. Eine solche Klasse kann eine vollstaendig eigene Aggregation-Logik definieren, etwa eine verschachtelte Aggregation fuer ein nested-Feld, das ueber die Standard-Filter-Klassen nicht abgedeckt wird.

9. Praxisbeispiel: eigenes Range-Facet fuer ein Custom-Attribut

Ein praktisches Beispiel ist ein Custom-Attribut "Lieferzeit in Tagen", das als eigenes Range-Facet in der Layered Navigation erscheinen soll, mit festen Buckets wie "sofort verfuegbar", "1-3 Tage" und "mehr als 3 Tage". Der Standard-Preisfilter von Magento ist fest auf das Preisfeld zugeschnitten, deshalb ist eine eigene Filter-Klasse der richtige Ansatz, die dieselbe Range-Aggregation-Logik auf das neue Attribut anwendet.


<?php
declare(strict_types=1);

namespace Mironsoft\SearchExtension\Model\Layer\Filter;

use Magento\Catalog\Model\Layer\Filter\AbstractFilter;
use Magento\Catalog\Model\Layer\Filter\FilterInterface;
use Magento\Framework\App\RequestInterface;

/**
 * Custom layered navigation filter for the "delivery_days" attribute,
 * exposed as fixed range buckets instead of the default terms aggregation.
 */
class DeliveryTimeFilter extends AbstractFilter implements FilterInterface
{
    private const REQUEST_VAR = 'delivery_days';

    /**
     * Applies the selected delivery time range to the product collection.
     *
     * @param RequestInterface $request
     * @return $this
     */
    public function apply(RequestInterface $request): self
    {
        $filterValue = $request->getParam(self::REQUEST_VAR);
        if (!$filterValue) {
            return $this;
        }

        [$from, $to] = array_pad(explode('-', (string) $filterValue), 2, null);
        $this->getLayer()->getProductCollection()->addFieldToFilter(
            'delivery_days',
            array_filter(['from' => $from, 'to' => $to])
        );

        return $this;
    }
}

Mironsoft

Layered Navigation, Facettensuche und Elasticsearch-Aggregationen

Filter, die schnell bleiben, auch bei grossen Katalogen?

Wir bauen eigene Facetten fuer Custom-Attribute, optimieren bestehende Aggregation-Requests und finden die Ursache, wenn die Layered Navigation unter Last langsam wird.

Custom-Facetten

Eigene Filter-Klassen fuer Attribute ausserhalb des Standard-Schemas

Performance-Analyse

Aggregation-Requests profilieren und unnoetige Facetten reduzieren

Mapping-Review

Sicherstellen, dass Attribute korrekt fuer Aggregationen gemappt sind

10. Zusammenfassung

Die Layered Navigation in Magento ist keine eigenstaendige Datenquelle, sondern eine visuelle Schicht ueber Elasticsearch-Bucket-Aggregationen. Diskrete Attribute wie Marke oder Farbe werden ueber Terms-Aggregationen auf keyword-Feldern abgebildet, numerische Attribute wie der Preis ueber Range- oder Histogram-Aggregationen. Beide laufen im selben Request wie die eigentliche Produktsuche, kombiniert mit selektiven Filter-Aggregationen, die bereits aktivierte Filter fuer die jeweils andere Facette korrekt beruecksichtigen.

Wer eigene Facetten fuer Custom-Attribute ergaenzen will, muss sowohl das Elasticsearch-Mapping als auch eine passende Filter-Klasse im Layer-Modul bereitstellen. Performance-Probleme in der Layered Navigation entstehen fast immer durch zu viele gleichzeitig berechnete Facetten mit zu hohem size-Wert oder durch Felder ohne doc_values. Wer diese Zusammenhaenge kennt, kann Facetten gezielt erweitern, statt bei jedem neuen Filterwunsch im Frontend-Template zu improvisieren.

Layered Navigation und Aggregationen: das Wichtigste auf einen Blick

Terms-Aggregation

Fuer diskrete Attribute wie Marke und Farbe, arbeitet ausschliesslich auf keyword-Feldern.

Range-Aggregation

Fuer den Preisfilter, Intervalle dynamisch nach Produktzahl oder gleicher Preisspanne berechnet.

Ein Request fuer alles

Produktsuche und alle Facetten laufen kombiniert in einem einzigen Elasticsearch-Request.

Performance

doc_values aktiv halten, Anzahl gleichzeitiger Facetten und Bucket-Groesse bewusst begrenzen.

11. FAQ: Layered Navigation und Aggregationen

1Wie haengen Navigation und Aggregation zusammen?
Jede Filteroption ist ein Bucket aus einer Aggregation, berechnet im selben Request wie die Produktsuche.
2Warum kein Terms-Facet auf text-Feldern?
Text wird analysiert und tokenisiert. Terms braucht exakte Werte, also ein keyword-Feld.
3Wie werden Preisintervalle berechnet?
Ueber Price Navigation Step Calculation, mit ausgeglichener Produktzahl oder gleich grossen Spannen.
4Warum fehlen manche Filterwerte?
Der size-Parameter der Terms-Aggregation liegt standardmaessig bei zehn und muss bei Bedarf erhoeht werden.
5Was macht der LayerResolver?
Entscheidet zwischen Category-Layer und Search-Layer, beide nutzen dieselbe Aggregationslogik.
6Warum bleibt die Markenliste vollstaendig?
Der eigene Filter wird fuer die eigene Aggregation ausgeklammert, ueber eine separate filter-Aggregation.
7Warum sind viele Facetten langsam?
Jede Aggregation zaehlt zusaetzlich ueber die Ergebnismenge, das summiert sich bei vielen gleichzeitig.
8Was ist doc_values?
Spaltenorientierte Datenstruktur, die Aggregationen ueberhaupt effizient moeglich macht.
9Reicht is_filterable allein?
Nein, das Mapping muss ebenfalls korrekt sein, sonst liefert die Aggregation falsche Buckets.
10Wann eine eigene Filter-Klasse?
Bei abweichender Bucket-Struktur, festen Range-Buckets oder nested-Feldern ausserhalb des Standard-Schemas.