AACsearch
Such-API

Multi-Suche & Abfragen

Mehrere Suchanfragen in einer einzigen Anfrage für Autocomplete, föderierte Suche und Multi-Index-Szenarien ausführen.

Multi-Suche führt mehrere Suchanfragen in einem einzigen HTTP-Round-Trip aus. Dies ist unverzichtbar für Autocomplete (gleichzeitig eine Ergebnisabfrage + eine Vorschlagsabfrage ausführen) und föderierte Suche (gleichzeitig über mehrere Indizes suchen).

Multi-Suche-Endpunkt

Endpunkt: POST /api/search/multi

Auth: Authorization: Bearer ss_search_your_key

{
	searches: Array<{
		indexSlug: string;
		q: string;
		queryBy?: string;
		filterBy?: string;
		facetBy?: string;
		sortBy?: string;
		page?: number;
		perPage?: number;
		highlightFields?: string;
	}>;
}

Antwort:

{
	results: Array<SearchResponse>; // ein Ergebnis pro Suche in derselben Reihenfolge
}

Autocomplete-Muster

Der häufigste Multi-Suche-Anwendungsfall: gleichzeitig eine vollständige Suche plus eine schnelle Vorschlagsabfrage ausführen.

const [mainResults, suggestions] = await client.multiSearch([
	{
		indexSlug: "products",
		q: "kabellose kopf",
		queryBy: "title,brand,description",
		perPage: 20,
		facetBy: "brand,categories",
	},
	{
		indexSlug: "products",
		q: "kabellose kopf",
		queryBy: "title",
		perPage: 5,
		includeFields: "id,title",
		highlightFields: "title",
	},
]);

Dies sendet eine einzige HTTP-Anfrage und erhält beide Ergebnismengen gleichzeitig zurück.

Föderierte Suche über Indizes

Über mehrere Indizes suchen und Ergebnisse clientseitig zusammenführen:

const [productResults, articleResults] = await client.multiSearch([
	{
		indexSlug: "products",
		q: "nachhaltigkeit",
		queryBy: "title,description,tags",
		perPage: 10,
	},
	{
		indexSlug: "knowledge",
		q: "nachhaltigkeit",
		queryBy: "content,title",
		perPage: 5,
	},
]);

Hinweis: Für indexübergreifende Ergebnisse clientseitig zusammenführen und neu bewerten.

Abfrageparameter im Detail

q — Suchanfrage

Die Suchzeichenkette. Verwenden Sie "*" für eine Platzhalter-Durchsuchen-Funktion (kein Volltextabgleich, nützlich für gefiltertes Durchsuchen).

{ "q": "kabellose kopfhörer" }    // Volltextsuche
{ "q": "*" }                      // Alle durchsuchen (mit filterBy kombinieren)

queryBy — welche Felder durchsucht werden

Kommagetrennte Feldliste. Die Reihenfolge ist wichtig — frühere Felder erhalten standardmäßig eine höhere Gewichtung.

{ "queryBy": "title,sku,brand,description" }

Für jedes Feld können Sie eine Feldgewichtungsüberschreibung angeben:

{ "queryBy": "title,brand,description", "queryByWeights": "10,5,2" }

filterBy — Filterausdruck

Boolescher Ausdruck unter Verwendung der Filtersyntax:

Gleichheit:      field:=value
                 field:=[value1, value2]
Bereich:         field:>100
                 field:[50..200]
Exakter String:  field:=value
Negation:        field:!=value
Logisches UND:   cond1 && cond2
Logisches ODER:  cond1 || cond2
Gruppierung:     (cond1 || cond2) && cond3

Beispiele:

"filterBy": "availability:=in_stock"
"filterBy": "price:[50..200] && brand:=[Sony, Bose]"
"filterBy": "categories:=Electronics && availability:!=out_of_stock"

facetBy — Facettenzählungen berechnen

Gibt Werteverteilungen für die angegebenen Felder zurück. Facettenfelder müssen im Collection-Schema als facet: true indiziert sein.

{ "facetBy": "brand,categories,availability" }

Die Antwort enthält facetCounts mit Wert-→-Anzahl-Paaren für jedes Facettenfeld.

sortBy — Sortierreihenfolge

Einzel- oder Mehrfeld-Sortierung:

{ "sortBy": "price:asc" }
{ "sortBy": "_text_match:desc,price:asc" }
{ "sortBy": "created_at:desc" }

Verfügbare Sortierfelder: price, sale_price, created_at, _text_match (Relevanzbewertung).

Tippfehlertoleranz

Die Tippfehlertoleranz von AACSearch wird automatisch angewendet. Standardmäßig:

  • 1 Tippfehler für Abfragen mit 5+ Zeichen erlaubt
  • 2 Tippfehler für Abfragen mit 8+ Zeichen erlaubt

Dies behandelt häufige Rechtschreibfehler wie „kopfhörer" → passt zu „kopfhörer".

Hervorhebung

Übereinstimmende Begriffe werden mit konfigurierbaren Tags hervorgehoben:

{
	"highlightFields": "title,description",
	"highlightStartTag": "<em class='highlight'>",
	"highlightEndTag": "</em>"
}

Hervorhebungen in der Antwort:

{
	"highlights": [
		{
			"field": "title",
			"snippet": "Sony <em class='highlight'>Kabellose</em> <em class='highlight'>Kopfhörer</em>"
		}
	]
}

Seitenumbruch

Ergebnisse werden mit page (1-indiziert) und perPage paginiert:

{ "page": 2, "perPage": 20 }

Die Antwort enthält found (Gesamtzahl übereinstimmender Treffer) für die Berechnung der Seitengesamtzahl:

const totalPages = Math.ceil(results.found / perPage);

Maximum perPage: 100. Für Exporte, die mehr Ergebnisse erfordern, cursorbasierte Iteration mit steigenden page-Werten implementieren.

Leistungsempfehlungen

  • perPage ≤ 20 für Suche-während-der-Eingabe (Autocomplete) beibehalten
  • includeFields verwenden, um nur die gerenderten Felder zurückzugeben — reduziert die Antwortgröße
  • Keine Facetten bei jedem Tastendruck ausführen — Entprellung bei 150–300 ms
  • Für große Ergebnismengen mit vielen Facetten die Facettenabfrage von der Hauptabfrage trennen mittels Multi-Suche

Limits

LimitValue
Max searches per multi-search request20 — enforced in public-handler.ts.
Max perPage100
Max page1000
Max numTypos3
Max rangeFacets per search20

Exceeding the multi-search batch limit returns 400 invalid_input with the Zod path searches. Use two consecutive requests rather than batching past 20.

Partial failures

A multi-search request is atomic at the HTTP level — sub-searches can still fail individually inside a 200. Each result envelope carries its own status: success carries hits, found, page; failure carries error and code. Iterate the results array in order; treat any entry with error as a sub-search failure. A single sub-search failure does not roll back the others.

Common error reasons: collection_not_found, invalid_filter, not_authorized (scoped token mismatch), rate_limit_exceeded, internal_error. Engine errors are normalised before reaching the client (Invariant 6 — never echoed raw).

Shared authentication and quota

Authentication is per request, not per sub-search. The same Authorization: Bearer ss_search_* (or ss_scoped_*) header validates the entire batch. A scoped token's scopedFilter is AND-combined into every sub-search (Invariant 4).

Quota / rate limit treats the batch as a single HTTP unit but charges per sub-search. A 20-batch is 20× the unit cost of a single search.

Tenant isolation (Invariant 5) holds at the sub-search level — every sub-search has tenantId AND-combined automatically.

Analytics grouping

Each multi-search response carries a parent queryId (the whole batch) and a per-sub-result queryId. Click events posted via POST /events/track should carry the child queryId so per-search CTR computes correctly. Conversion events follow the same rule. See Analytics overview.

A common bug is to record clicks against the first sub-search's queryId regardless of which list the click came from — that inflates the suggestions-list CTR and deflates the main-results CTR. Always pass through the specific child queryId.

Caching and debouncing

Recommended values (matching the bundled widget): keystroke debounce 200 ms (range 150–300 ms), min characters 2, client-side LRU 30 entries scoped to indexSlug, popular-queries TTL 10 min.

Do not extend the server-side response cache without a rate-limit-aware invalidation path — stale suggestions can keep linking to products that have been removed by a curation change.

Federated suggestions example

Combining a primary product search with a categories suggestion and an articles suggestion in one round-trip:

const [products, categories, articles] = await client.multiSearch([
  { indexSlug: "products",  q: "wireless head", queryBy: "title,brand,description", perPage: 20, facetBy: "brand,categories" },
  { indexSlug: "categories", q: "wireless head", queryBy: "name,aliases",            perPage: 3,  includeFields: "id,name,slug" },
  { indexSlug: "articles",   q: "wireless head", queryBy: "title,excerpt",           perPage: 3,  includeFields: "id,title,url,published_at" },
]);

Three sub-searches still count as three units against the rate-limit bucket — budget accordingly.

On this page