Wie Suche wirklich zusammenspielt
Wenn eine Magento-GraphQL-Produktsuche langsam ist, liegt das Problem selten in der GraphQL-Schicht. Es liegt fast immer darin, wie Suchanfragen durch Resolver an OpenSearch weitergeleitet werden — und ob Indexierung, Filter-Mapping und Sortierung korrekt konfiguriert sind.
Inhaltsverzeichnis
- 1. Die Sucharchitektur in Magento: vom GraphQL-Request bis OpenSearch
- 2. Indexierung: Was OpenSearch über Produkte weiß
- 3. Der Produkt-Resolver: Wie er zwischen DB und OpenSearch entscheidet
- 4. Filter-Mapping: GraphQL-Argumente in OpenSearch-Queries übersetzen
- 5. Suchanfragen analysieren: Queries debuggen und optimieren
- 6. Aggregations: Wie Facetten-Filter aus OpenSearch kommen
- 7. DB-Suche vs. OpenSearch: Was wirklich langsamer ist und warum
- 8. Eigene Attribute für die Suche konfigurieren
- 9. Typische Fehlerbilder: Wenn Suche nicht das zurückgibt, was erwartet wird
- 10. Zusammenfassung
- 11. FAQ
1. Die Sucharchitektur in Magento: vom GraphQL-Request bis OpenSearch
Wenn ein Headless-Frontend eine GraphQL-Produktsuche absetzt (products(search: "...")), durchläuft die Anfrage mehrere Schichten, bevor das Ergebnis zurückkommt. Der Resolver empfängt die Argumente, erstellt ein SearchCriteria-Objekt und übergibt es an den Produkt-Repository. Sobald ein search-Argument vorhanden ist, leitet Magento die Anfrage automatisch an den Suchadapter weiter — in Magento 2.4 standardmäßig OpenSearch. Dieser Adapter übersetzt das SearchCriteria-Objekt in eine OpenSearch-Query, sendet sie an den OpenSearch-Cluster und gibt die Ergebnis-IDs zurück.
Die zurückgegebenen IDs werden danach in einer zweiten Datenbankabfrage verwendet, um die vollständigen Produktdaten zu laden — Attribute, die nicht im OpenSearch-Index sind, werden aus der Datenbank nachgeladen. Das ist ein wichtiger Punkt: OpenSearch gibt nicht die vollständigen Produktdaten zurück, sondern primär IDs und Relevanz-Scores. Magento kombiniert OpenSearch-Ergebnisse und Datenbankdaten in der finalen Response. Diese Architektur ist transparent für den GraphQL-Client, aber für Performance-Optimierungen muss man beide Schichten kennen.
Der Suchadapter ist dabei auswechselbar. Magento abstrahiert den konkreten Suchdienst hinter Interfaces, sodass ein Wechsel von OpenSearch zu einem anderen Suchdienst theoretisch ohne Änderungen an den Resolvern möglich ist. In der Praxis wird dieses Versprechen durch Adapter-spezifische Konfigurationen eingeschränkt — aber das Grundprinzip der Schichtentrennung ist solide.
2. Indexierung: Was OpenSearch über Produkte weiß
OpenSearch kennt nur, was Magento in den Index geschrieben hat. Der Catalog-Index enthält für jedes Produkt eine Reihe von Standardfeldern: SKU, Name, Beschreibung, Preis, Status, Sichtbarkeit und alle konfigurierten searchable und filterable Attribute. Nicht im Index sind: komplexe EAV-Beziehungen, Custom Options, konfigurierbare Varianten-Details und alle Attribute, die in der Admin-Konfiguration nicht als Used in Layered Navigation oder Searchable markiert sind.
Das hat direkte Auswirkungen auf GraphQL-Filter. Ein Filter auf ein Attribut, das nicht im Index ist, zwingt Magento zurück auf einen Datenbank-Fallback — und dieser Fallback verliert alle Vorteile von OpenSearch: Relevanz-Scoring, schnelle Volltext-Suche und Aggregations für Facetten-Filter. Deshalb ist die Indexierungskonfiguration nicht nur eine Admin-Einstellung, sondern eine architektonische Entscheidung, die direkt die Performance von GraphQL-Suchabfragen beeinflusst.
3. Der Produkt-Resolver: Wie er zwischen DB und OpenSearch entscheidet
Magento's interner Produkt-Resolver prüft, ob die eingehende Query ein search- oder ein filter-Argument enthält. Bei einem search-Argument wird zwingend der Suchadapter (OpenSearch) verwendet. Bei einem reinen filter-Argument hängt es davon ab, ob die Filter auf indexierten Attributen basieren oder nicht. Wenn alle Filter auf filterable-Attributen basieren, kann OpenSearch die Filter-Abfrage ebenfalls übernehmen — mit erheblichen Performance-Vorteilen gegenüber DB-Queries.
Das Switching-Verhalten ist nicht immer transparent. In Magento-Logs sieht man nicht direkt, welcher Pfad genommen wurde — man muss das an der Query-Struktur und den Response-Zeiten ableiten. Ein einfacher Diagnosetest: dieselbe Query mit search: "" (leerer Suchstring) und ohne search-Argument absenden und die Antwortzeiten vergleichen. Wenn der Unterschied erheblich ist, läuft einer der beiden Pfade über die Datenbank statt über OpenSearch.
# Query type 1: full-text search — always uses OpenSearch
query SearchProducts {
products(
search: "running shoes"
filter: {
price: { from: "30.00", to: "200.00" }
}
pageSize: 24
currentPage: 1
sort: { relevance: DESC }
) {
total_count
aggregations {
attribute_code
label
count
options {
label
value
count
}
}
items {
sku
name
url_key
}
}
}
# Query type 2: filter-only — may or may not use OpenSearch
# depends on whether filter attributes are indexed
query FilterProducts {
products(
filter: {
category_id: { eq: "5" }
price: { from: "50.00", to: "150.00" }
}
pageSize: 24
) {
total_count
items {
sku
name
}
}
}
4. Filter-Mapping: GraphQL-Argumente in OpenSearch-Queries übersetzen
Wenn ein GraphQL-Filter an OpenSearch weitergeleitet wird, übersetzt Magento's Suchadapter das SearchCriteria-Objekt in eine OpenSearch-Query DSL. Einfache Gleichheitsfilter werden zu term-Queries, Bereichsfilter zu range-Queries, und Volltext-Suchen zu multi_match- oder query_string-Queries. Diese Übersetzung ist automatisch — aber nur, wenn das gefilterte Attribut korrekt im OpenSearch-Mapping konfiguriert ist.
Das OpenSearch-Feldmapping definiert, wie Magento ein Produktattribut im Index speichert. Textfelder können als text (für Volltext-Suche mit Tokenisierung) oder als keyword (für exakte Filter-Abfragen) gespeichert werden — oft beides gleichzeitig als Multi-Field. Filterbare Attribute werden typischerweise als keyword gespeichert, damit sie für präzise term-Queries verwendbar sind. Wenn ein Attribut fehlt oder falsch gemappt ist, liefert der OpenSearch-Filter kein Ergebnis — ohne Fehlermeldung, nur mit leerem Ergebnissatz.
5. Suchanfragen analysieren: Queries debuggen und optimieren
Das effektivste Werkzeug zum Debuggen von Magento-OpenSearch-Anfragen ist das OpenSearch-Query-Logging. Über die OpenSearch-Admin-Oberfläche oder direkt über die REST-API lassen sich die tatsächlichen Query-DSL-Objekte einsehen, die Magento an OpenSearch sendet. Diese Information zeigt, welche Filter übersetzt wurden, welche Relevanz-Boosts angewendet werden und ob die Query die erwarteten Treffer erzeugt.
Ein weiteres wichtiges Diagnose-Tool ist Magento's eigenes bin/magento indexer:status. Wenn der Catalog-Index nicht aktuell ist, liefert OpenSearch veraltete oder fehlende Produkte — ohne Fehlermeldung in der GraphQL-Response. Performance-Probleme, die sich durch langsame Response-Zeiten trotz korrekter OpenSearch-Konfiguration äußern, liegen häufig in der zweiten Phase: dem Nachladen von Produkt-Details aus der Datenbank für Attribute, die nicht im Index sind.
# Debug query: check if aggregations (facets) are returned correctly
# Aggregations are only available when using OpenSearch
query DebugSearchWithAggregations {
products(
search: "jacket"
filter: {
category_id: { eq: "12" }
}
pageSize: 1
currentPage: 1
) {
total_count
# If aggregations are empty or missing, OpenSearch is not being used
# or the indexed attributes are not configured as filterable
aggregations {
attribute_code
label
count
options {
label
value
count
}
}
# page_info helps verify pagination works correctly with OpenSearch
page_info {
current_page
page_size
total_pages
}
items {
sku
}
}
}
# Expected: aggregations contains price, color, size, etc.
# Missing aggregations = OpenSearch not engaged or attribute not indexed
6. Aggregations: Wie Facetten-Filter aus OpenSearch kommen
Das aggregations-Feld in Magento's GraphQL-Schema ist eine der wichtigsten Features der OpenSearch-Integration. Es liefert für jedes filterbare Attribut eine Liste von verfügbaren Werten mit der Anzahl der Treffer pro Wert — das ist die Grundlage für dynamische Facetten-Filter im Headless-Frontend. Diese Daten kommen direkt aus OpenSearch's Aggregations-Funktion und sind ein einziges Query-Ergebnis — kein separater API-Call pro Filterattribut.
Aggregations funktionieren nur, wenn OpenSearch für die Anfrage verwendet wird. Bei einem reinen Datenbank-Fallback gibt das aggregations-Feld ein leeres Array zurück. Das ist einer der häufigsten Fehler beim Aufbau von Headless-Frontends: Das Frontend erwartet Aggregationen für Facetten-Filter, aber weil ein Filter auf einem nicht-indizierten Attribut den OpenSearch-Pfad umgeht, kommen keine Aggregationen zurück. Die Lösung ist immer dieselbe: Das Filterattribut in der Admin-Konfiguration als Used in Layered Navigation aktivieren und den Index neu aufbauen.
7. DB-Suche vs. OpenSearch: Was wirklich langsamer ist und warum
| Kriterium | Datenbank-Suche | OpenSearch | Vorteil |
|---|---|---|---|
| Volltext-Suche | LIKE-Query, langsam | Invertierter Index, schnell | OpenSearch: 10-100x schneller |
| Facetten-Aggregationen | Separate COUNT-Queries pro Attribut | Eine Aggregation, alle Werte | OpenSearch: O(1) statt O(n) |
| EAV-Filter | JOIN über mehrere Tabellen | Denormalisiert im Index | OpenSearch: keine Joins nötig |
| Relevanz-Ranking | Nicht vorhanden | TF/IDF und BM25 | Bessere Suchergebnisse |
| Skalierbarkeit | Lineare Degradierung bei großen Katalogen | Horizontale Skalierung | OpenSearch: Cluster skalierbar |
Die Tabelle macht deutlich: OpenSearch ist für alle Suchszenarien in Magento deutlich überlegen. Das einzige Szenario, in dem die Datenbank noch eine Rolle spielt, ist das Nachladen von Produkt-Detaildaten, die nicht im Index sind. Genau deshalb ist die Indexierungsstrategie so wichtig: Je mehr relevante Produktdaten im Index sind, desto weniger DB-Abfragen sind für eine vollständige GraphQL-Response nötig.
8. Eigene Attribute für die Suche konfigurieren
Eigene Produktattribute aus Custom Modules sind standardmäßig nicht im OpenSearch-Index enthalten. Damit ein benutzerdefiniertes Attribut in GraphQL-Filtern verwendet werden kann und Aggregationen liefert, müssen drei Konfigurationsschritte durchgeführt werden. Erstens: Das Attribut muss in der Admin-Oberfläche als Used in Layered Navigation aktiviert werden (für Facetten-Filter) und/oder als Use in Search (für Volltext-Suche). Zweitens: Das Attribut muss zur Schema-Graphqls-Datei des Moduls hinzugefügt werden, damit es im GraphQL-Filter-Input verfügbar ist. Drittens: Der Catalog-Index muss neu aufgebaut werden (bin/magento indexer:reindex catalogsearch_fulltext).
Ein häufig übersehener vierter Schritt: Das OpenSearch-Feldmapping muss überprüft werden. Wenn ein Attribut mit einem ungeeigneten Datentyp im Index gespeichert wird — etwa ein numerisches Attribut als String —, funktionieren Bereichsfilter nicht korrekt. Das Mapping kann über die OpenSearch-REST-API eingesehen werden (GET /magento2_product_1/_mapping) und muss gegebenenfalls nach einer Index-Rekonfiguration neu aufgebaut werden.
9. Typische Fehlerbilder: Wenn Suche nicht das zurückgibt, was erwartet wird
Das häufigste Fehlerbild: Eine Volltext-Suche gibt zu viele oder zu wenige Ergebnisse zurück. Der Grund liegt fast immer in der Gewichtung der Suchfelder im OpenSearch-Index. Magento konfiguriert standard Gewichtungen für Name, Beschreibung und SKU — aber Custom Attributes, die als Suchfelder konfiguriert sind, erhalten oft zu hohe oder zu niedrige Gewichtungen. Das Ergebnis sind irrelevante Treffer auf den ersten Positionen oder gewünschte Produkte, die gar nicht erscheinen.
Ein zweites Fehlerbild: Kategoriefilter liefert falsche Ergebnisse. In Magento wird die Kategorienzugehörigkeit im Index als Array von Kategorie-IDs gespeichert — inklusive aller Elternkategorien. Wenn ein GraphQL-Filter eine Kategorie-ID verwendet, die im Index nicht in dieser Form vorhanden ist (etwa weil der Category-Index veraltet ist), gibt die Suche leere Ergebnisse zurück. Der Fix ist simpel: bin/magento indexer:reindex catalog_category_product und danach catalogsearch_fulltext.
# Diagnostic query: verify custom attribute is indexed and filterable
query CheckCustomAttributeFilter {
products(
filter: {
# custom_material must be configured as:
# - "Used in Layered Navigation: Filterable"
# - "Use in Search: Yes" (if full-text searchable)
custom_material: { eq: "cotton" }
}
pageSize: 5
) {
total_count
aggregations {
attribute_code
label
# custom_material should appear here if correctly indexed
}
items {
sku
name
# custom_material field must be added to schema.graphqls
# and ProductInterface extension to appear here
}
}
}
# If total_count is 0 and you expect results:
# 1. Check attribute configuration in Magento Admin
# 2. Verify field mapping: GET /magento2_product_1/_mapping
# 3. Run: bin/magento indexer:reindex catalogsearch_fulltext
# 4. Check GraphQL schema for filter input registration
10. Zusammenfassung
OpenSearch und Magento GraphQL arbeiten über mehrere Abstraktionsschichten zusammen: Der Resolver übersetzt GraphQL-Argumente in SearchCriteria-Objekte, der Suchadapter übersetzt diese in OpenSearch-Query-DSL-Objekte, und das Ergebnis wird mit Datenbankdaten kombiniert, bevor es als GraphQL-Response zurückgegeben wird. Diese Architektur ist leistungsfähig, erfordert aber Kenntnisse auf allen Ebenen für eine korrekte Konfiguration und Performance-Optimierung.
Die wichtigsten Hebel für performante GraphQL-Suche in Magento: Alle relevanten Filterattribute als filterable konfigurieren, den Index aktuell halten, Aggregationen für Facetten-Filter nutzen und die Nachlad-Schicht aus der Datenbank minimieren, indem möglichst viele Felder im Index vorhanden sind. Das ist kein einmaliger Setup-Schritt — Indexierungsstrategie und OpenSearch-Konfiguration müssen bei jedem neuen Attribut und jedem neuen Custom Module mitgedacht werden.
OpenSearch und Magento GraphQL — Das Wichtigste auf einen Blick
Architektur
Resolver → SearchCriteria → Suchadapter → OpenSearch-Query → IDs → Datenbankabfrage für Details.
Indexierung
Nur indexierte Attribute können als GraphQL-Filter ohne DB-Fallback verwendet werden. Aggregations erfordern OpenSearch-Pfad.
Konfiguration
Attribut als filterable markieren, schema.graphqls erweitern, Index neu aufbauen. Drei Schritte für jeden Custom Filter.
Diagnose
Leere Aggregations = OpenSearch nicht aktiv. Leere Ergebnisse = Index veraltet oder Attribut-Mapping falsch.