AACsearch
Such-API

Filter, Sortierung & Seitenumbruch

Vollständige Referenz für Filterausdrücke, Sortieroptionen, Facettierung und Seitenumbruch in AACsearch-Abfragen.

AACsearch verwendet die Filter- und Sortiersyntax von AACSearch mit einigen produktschema-spezifischen Konventionen. Diese Seite ist eine vollständige Referenz für das Erstellen von Filterausdrücken, das Konfigurieren von Sortierreihenfolgen und das Paginieren von Ergebnissen.

Filterausdruck-Syntax

Filter werden als Zeichenkette mit boolescher Logik angegeben.

Gleichheit

availability:=in_stock
brand:=Sony
categories:=Electronics
locale:=de

Negation

availability:!=out_of_stock
brand:!=Competitors

Bereich (numerisch)

price:>50
price:<200
price:[50..200]     # inklusiver Bereich
price:(50..200)     # exklusiver Bereich

Array-Mitgliedschaft

categories:=[Electronics, Audio]
brand:=[Sony, Bose, Sennheiser]

Zeichenkette enthält / Präfix

AACSearch unterstützt nur exakten Zeichenkettenabgleich für Facettenfelder. Für Präfix-Suche verwenden Sie q mit queryBy.

Boolesche Logik

# UND (beide Bedingungen müssen übereinstimmen)
availability:=in_stock && price:<200

# ODER (eine der Bedingungen stimmt überein)
brand:=Sony || brand:=Bose

# Gruppierung
(brand:=Sony || brand:=Bose) && availability:=in_stock && price:<200

Mandantenfilter (automatisch)

Der öffentliche Handler hängt automatisch folgendes voran:

organization_id:={organizationId}

Sie müssen diesen Filter nicht manuell hinzufügen — er wird immer angewendet.

Verfügbare Filterfelder

Das Standard-Produktschema enthält diese filterbaren Felder (als facet: true markiert):

FeldTypBeispielfilter
brandstringbrand:=Sony
categoriesstring[]categories:=[Electronics, Audio]
availabilitystringavailability:=in_stock
pricefloatprice:[10..500]
sale_pricefloatsale_price:&lt;100
localestringlocale:=de

Facettierung

Facetten berechnen die Werteverteilung für ein Feld über übereinstimmende Dokumente. Verwenden Sie sie zum Rendern von Filter-Seitenleisten.

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

Antwort enthält facetCounts:

{
	"facetCounts": [
		{
			"fieldName": "brand",
			"counts": [
				{ "value": "Sony", "count": 45 },
				{ "value": "Bose", "count": 28 },
				{ "value": "Sennheiser", "count": 19 }
			]
		},
		{
			"fieldName": "availability",
			"counts": [
				{ "value": "in_stock", "count": 89 },
				{ "value": "out_of_stock", "count": 23 }
			]
		}
	]
}

Facetten mit angewendeten Filtern

Wenn ein Filter aktiv ist, spiegeln Facettenzählungen die gefilterte Ergebnismenge wider, nicht den gesamten Index:

{
	"q": "*",
	"filterBy": "brand:=Sony",
	"facetBy": "categories,availability"
}

Dies gibt Kategorien und Verfügbarkeitszählungen nur für Sony-Produkte zurück.

Sortierung

Einzelfeld-Sortierung

{ "sortBy": "price:asc" }
{ "sortBy": "price:desc" }
{ "sortBy": "created_at:desc" }
{ "sortBy": "sale_price:asc" }

Relevanz-zuerst-Sortierung (Standard)

{ "sortBy": "_text_match:desc" }

Dies ist der Standard, wenn sortBy weggelassen wird.

Mehrfeld-Sortierung (Gleichstand-Auflösung)

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

Sortiert zuerst nach Relevanzbewertung; innerhalb derselben Bewertung nach Preis aufsteigend.

Häufige Sortier-Presets

BezeichnungsortBy-Wert
Relevanteste_text_match:desc
Preis: günstigste zuerstprice:asc
Preis: teuerste zuerstprice:desc
Neueste zuerstcreated_at:desc
Angebotspreis: günstigste zuerstsale_price:asc

Seitenumbruch

{
	"page": 1, // 1-indiziert, Standard: 1
	"perPage": 20 // Standard: 10, Max: 100
}

Antwortfelder für den Seitenumbruch:

{
	"found": 142, // Gesamtzahl übereinstimmender Dokumente
	"page": 1, // Aktuelle Seite
	"outOf": 5000 // Gesamtdokumente im Index (ungefiltert)
}

Gesamtseiten berechnen:

const totalPages = Math.ceil(results.found / perPage);
const hasMore = results.page * perPage < results.found;

Warnung bei tiefer Paginierung

Die Leistung verschlechtert sich bei großen page-Werten für Indizes über 100.000 Dokumente. Für tiefe Paginierung oder Exporte:

  1. Steigende page-Werte mit festem perPage verwenden
  2. Oder cursorbasierte Paginierung implementieren, indem ein eindeutiges Feld sortiert wird (id:asc) und filterBy: "id:>lastSeenId" verwendet wird

Feldauswahl

Reduzieren Sie die Antwortgröße, indem Sie nur die Felder auswählen, die Sie benötigen:

{
	"includeFields": "id,title,price,brand,availability",
	"excludeFields": "description,tags"
}

Verwenden Sie excludeFields, um große Textfelder aus Antworten zu entfernen, wenn Sie sie nicht rendern (z. B. description in einer Suche-während-der-Eingabe-Autocomplete, die nur Titel anzeigt).

Filter mit eingeschränkten Token kombinieren

Wenn ein eingeschränktes Token verwendet wird, wird sein scopedFilter UND-verknüpft mit dem filterBy des Aufrufers:

Token-scopedFilter:  "price:<100"
Aufrufer-filterBy:   "brand:=Sony && availability:=in_stock"
Effektiver filterBy: "brand:=Sony && availability:=in_stock && price:<100"

Der Filter des Tokens kann nicht vom filterBy des Aufrufers überschrieben werden.

Escaping values

Filter values are parsed by AACSearch's filter expression engine. Most strings work as-is, but a few characters have special meaning and must be backtick-quoted (or backslash-escaped) when they appear inside a value:

CharacterMeaning in the grammarInside a value, write as
,Array element separatorWrap value in `…`
&&AND operatorWrap value in `…`
||OR operatorWrap value in `…`
( )GroupingWrap value in `…`
[ ]Range / array delimitersWrap value in `…`
:Field/operator separatorWrap value in `…`
`The quote character itselfEscape with backslash: \`

Examples:

# Brand name contains a comma — must be quoted
brand:=`Ben & Jerry's`

# Category contains the AND operator literally
categories:=`Home && Garden`

User-controlled values must always be escaped before concatenation. Never interpolate user input into the filter string directly — use the safe filter builder.

Safe filter builder

Hand-concatenating a filter string from user input is a category-1 injection bug. Always build filters from a structured representation. A minimal TypeScript builder:

type AtomicFilter =
  | { field: string; op: "="; value: string | number | boolean }
  | { field: string; op: "!="; value: string | number | boolean }
  | { field: string; op: ">" | "<" | ">=" | "<="; value: number }
  | { field: string; op: "in"; values: Array<string | number> }
  | { field: string; op: "range"; min: number; max: number; inclusive?: boolean };

type FilterTree = AtomicFilter | { and: FilterTree[] } | { or: FilterTree[] };

const FIELD_RE = /^[a-z_][a-z0-9_]*$/;

function escapeValue(v: string | number | boolean): string {
  if (typeof v === "number" || typeof v === "boolean") return String(v);
  return "`" + String(v).replace(/`/g, "\\`") + "`";
}

function fieldOrThrow(field: string): string {
  if (!FIELD_RE.test(field)) throw new Error(`invalid field name: ${field}`);
  return field;
}

export function buildFilter(node: FilterTree): string {
  if ("and" in node) return node.and.map(buildFilter).join(" && ");
  if ("or" in node) return "(" + node.or.map(buildFilter).join(" || ") + ")";
  const f = fieldOrThrow(node.field);
  if (node.op === "in") return `${f}:=[${node.values.map(escapeValue).join(", ")}]`;
  if (node.op === "range") {
    const [lo, hi] = node.inclusive === false ? ["(", ")"] : ["[", "]"];
    return `${f}:${lo}${node.min}..${node.max}${hi}`;
  }
  return `${f}:${node.op}${escapeValue(node.value)}`;
}

The builder enforces two invariants: field names match a strict identifier regex, and every string value is backtick-quoted with inner backticks escaped. Replicate the same shape in your server SDK.

E-commerce filter recipes

Category page (in-stock only)

{
  "filterBy": "categories:=`Audio` && availability:=in_stock",
  "facetBy": "brand,price",
  "sortBy": "_text_match:desc,popularity_score:desc"
}

Multi-brand drill-down

filterBy: "categories:=[`Electronics`, `Audio`] && brand:=[`Sony`, `Bose`] && availability:=in_stock"

Price range with "on sale" branch

filterBy: "categories:=`Audio` && availability:=in_stock && (sale_price:[10..100] || (sale_price:<0 && price:[10..100]))"

Locale-aware listing

filterBy: "locale:=`en` && categories:=`Audio` && availability:=in_stock"

"More like this" (vector hybrid)

{
  "q": "*",
  "vectorQuery": "embedding:([…current vector…], k:50)",
  "filterBy": "id:!=`product-123` && availability:=in_stock && price:[1000..15000]",
  "perPage": 12
}

On this page