AACsearch
Architektur

Analyse-Feedback-Loop

Wie eine Suchanfrage zu einem SearchUsageEvent wird, wie Klicks und Conversions aufgezeichnet werden und wie die Aggregation das Dashboard für das Relevanz-Tuning speist.

Jede öffentliche Such-Interaktion emittiert eine Zeile in die vereinheitlichte Tabelle SearchUsageEvent. Eine einzige Tabelle mit type-Diskriminator (search_query, zero_results, click, conversion, widget_open, filter_applied, visit) hält das Schema klein und macht die Aggregation günstig.

End-to-End-Loop

Beschreibung

Das Diagramm zeigt, wie ein Suchaufruf, ein Ergebnis-Klick und eine Conversion alle in der vereinheitlichten SearchUsageEvent-Tabelle mit einer queryId-Korrelation landen und anschließend nach Index × Stunde × Typ zu CTR-, CVR- und Zero-Result-Rate-Metriken aggregiert werden. Das Studio-Analytics-Dashboard exponiert diese Metriken, und Editor-Aktionen (Synonyme, Curations, Feldgewichte, Presets) fließen über policy-cache zurück in den Read-Path — der Loop schließt sich ohne Deploy.

flowchart LR
    classDef http fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef db fill:#fef3c7,stroke:#b45309,color:#78350f
    classDef agg fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef ui fill:#ede9fe,stroke:#6d28d9,color:#4c1d95

    subgraph Stage1["1 · Query"]
        SDK1["Customer SDK / Widget"] --> Search["/api/search/public/multi"]:::http
        Search --> Hit["TS multi_search<br/>returns hits"]
        Hit --> Resp["Response sanitized<br/>queryId attached"]
        Resp --> Ev1["recordSearchUsageAsync<br/>type=search_query"]:::db
        Hit --> Zero{"hits.length == 0?"}
        Zero -- yes --> Ev2["type=zero_results"]:::db
    end

    subgraph Stage2["2 · Click"]
        SDK2["Widget / SDK click handler"] --> Track1["/api/events/track<br/>type=result_click"]:::http
        Track1 --> Ev3["type=click<br/>{ queryId, productId, position }"]:::db
    end

    subgraph Stage3["3 · Conversion"]
        SDK3["Checkout / add-to-cart"] --> Track2["/api/events/track<br/>type=conversion"]:::http
        Track2 --> Ev4["type=conversion<br/>{ queryId, productId, conversionType }"]:::db
    end

    subgraph Stage4["4 · Aggregation (async)"]
        Ev1 --> Agg["analytics aggregation<br/>group by indexId × hour × type"]:::agg
        Ev2 --> Agg
        Ev3 --> Agg
        Ev4 --> Agg
        Agg --> CTR["CTR = clicks / search_query"]
        Agg --> CVR["CVR = conversion / click"]
        Agg --> ZRR["zero-rate = zero_results / search_query"]
    end

    subgraph Stage5["5 · Dashboard refresh"]
        CTR --> Studio["apps/saas analytics dashboard"]:::ui
        CVR --> Studio
        ZRR --> Studio
        Studio --> Tune["relevance tuning:<br/>synonyms, curations,<br/>field weights, presets"]
    end

    Tune -. feeds .-> Search

Die Event-Typen

TypEmittiert vonTrägt
search_querypublic-handler.ts (fire-and-forget)query, filters, sort, locale, queryId, referrer, ua
zero_resultspublic-handler.ts bei found == 0gleiche Form wie search_query
result_clickclickWidget / SDK → /api/events/trackqueryId, productId, position, sessionId
widget_openHosted-Widget beim MountsessionId, anonymousUserId
filter_usedfilter_appliedWidget beim Facet-Umschaltenfilters (gewählte Facette/Wert), queryId
conversionCheckout-Integration → /api/events/trackqueryId, productId, conversionType (purchase, add_to_cart, ...)
visitOptionales Widget beim SeitenaufrufsessionId, anonymousUserId, referrer
click (roh)Nicht-suchgebundener KlickproductId, position

Alle Events teilen die Zeilenform { indexId, organizationId, type, count, metadata, createdAt }. Die JSON-Spalte metadata ist das einzige schema-flexible Feld; sie ist auf 4 KB begrenzt.

Was einen Klick mit einer Suche verbindet

Das serverseitige search_query-Event prägt eine stabile queryId und gibt sie auf der Antwort zurück. Das Widget gibt die queryId bei jedem nachgelagerten click-/ conversion-Event zurück. Die Aggregation verbindet auf (organizationId, indexId, queryId), um Click-Through und Conversion pro Query und pro Ranking-Slot zu berechnen.

Tuning-Loop

Das Studio-Analyse-Dashboard zeigt die drei Kernquoten (CTR, CVR, Zero-Rate) plus Per-Query-Drilldowns. Ein Editor kann darauf reagieren, indem er:

  • eine Synonym- oder Stemming-Regel für eine Hoch-Zero-Result-Query hinzufügt,
  • eine SKU per Curation Set für eine unter-geklickte Query promotet,
  • Feldgewichte anpasst oder ein Preset für eine langsam konvertierende Query baut.

Diese Bearbeitungen fließen über policy-cache (60 s LRU) in den Lesepfad — der Loop schließt sich ohne Deploy.

Verwandt

  • Lesepfad — wo recordSearchUsageAsync sitzt.
  • Konnektor-Lebenszyklus — das Ingest-seitige Gegenstück, das die Dokumente erzeugt, die durchsucht und geklickt werden.

On this page