AACSearch / guide
Events and recommendations
Send behavioural events from the storefront, configure recommendation models on a collection, and fetch similar, bought-together, trending and personal recommendations.
Recommendations are learned from what visitors do, so the work has two halves: send events from the storefront, and ask a model for recommendations. Both calls are search-scoped and can be made from a browser with a search or browser key (see keys and scopes). Creating and changing models needs a full key.
Events
ingestEvents — POST /events — records a batch of 1–500 events:
await client.events.ingestEvents({
events: [
{ type: "search", collection: "products", session_id, q: "drill", found: 0, query_id },
{ type: "click", collection: "products", session_id, query_id, doc_ids: ["42"], positions: [3] },
{
type: "purchase",
collection: "products",
session_id,
doc_ids: ["42"],
value: 12990,
currency: "RUB",
event_id: crypto.randomUUID(),
},
],
});Each event needs a type and a collection. type is one of search, view,
click, add_to_cart, purchase, conversion, impression, favorite,
wishlist, share, dismiss, suggestion_impression, suggestion_select,
chip_click, correction_accept, correction_reject.
| Field | Use |
|---|---|
session_id | the browsing session — required for session and co-visitation models (related_items, bought_together, session) |
user_id / anonymous_id | a signed-in user's stable id, or a device/cookie id |
doc_ids, positions | documents involved, and their 1-based positions in the list they were shown in |
query_id | the search or recommendations response the visitor acted on — attributes clicks and purchases |
q, found, filter_by | for search events; zero-hit queries (found: 0) feed synonym suggestions |
value, currency, objects | money: total, ISO 4217 code, per-document price and quantity |
timestamp | Unix milliseconds; defaults to receipt time |
event_id | your idempotency key (a UUID) — a repeat is dropped, not stored twice |
analytics_tag | a free-form label to segment analytics |
The API validates the batch immediately and stores it asynchronously. It
answers 202:
{ "accepted": 2, "deduplicated": 1, "rejected": [{ "index": 4, "reason": "…" }] }A rejected entry is reported by its index in the batch; the rest are stored.
Retrying a batch is safe when every event has an event_id.
Use one query_id for a results page and send it with the clicks and purchases
that come from it. The shipped recommendation widgets send the model id as
query_id on the clicks they record.
Recommendation models
A model lives on a collection:
| Operation | Endpoint |
|---|---|
upsertRecommendationModel | PUT /collections/{collectionName}/recommendation_models/{modelId} |
getRecommendationModel, deleteRecommendationModel | the same path with GET / DELETE |
listRecommendationModels | GET /collections/{collectionName}/recommendation_models |
await client.recommendations.upsertRecommendationModel({
collectionName: "products",
modelId: "similar",
body: { type: "looking_similar", vector_field: "embedding", filter_by: "in_stock:true" },
});
await client.recommendations.upsertRecommendationModel({
collectionName: "products",
modelId: "trending",
body: {
type: "trending",
event_weights: { view: 1, add_to_cart: 3, purchase: 5 },
decay_half_life_hours: 48,
window_days: 14,
},
});type | Recommends | Anchor at request time |
|---|---|---|
looking_similar | nearest neighbours in vector_field | object_id |
related_items | documents viewed in the same sessions | object_id |
bought_together | documents bought in the same sessions | object_id |
trending | most recent weighted interactions | none |
popular | plain weighted counts over the window, no decay | none |
session | neighbours of the average vector of what the session touched | session_id or user_id |
query_to_item | documents clicked or bought soon after searching query | query |
upsell | same-category items priced above the anchor (upsell_max_ratio) | object_id |
cross_sell / complementary | items the product graph and co-purchases link to the anchor | object_id |
substitutes / alternatives | same-category items within price_tolerance of the anchor's price | object_id |
recently_viewed | what the subject last engaged with | user_id, anonymous_id or session_id |
recently_searched | the subject's recent queries, returned in queries | user_id, anonymous_id or session_id |
sequential | next items from A→B transitions within sessions | anchors or the subject |
graph | walks edge_kinds edges 1–hops hops from the anchors | object_id / object_ids |
Common settings: event_weights (per event type; unset types count 0),
window_days (default 30), decay_half_life_hours (for trending, default 72),
filter_by (applied to every result), exclude_filter_by (never recommend
these), category_field and price_field (for the category- and price-based
types), pins for guaranteed placements.
Get recommendations
getRecommendations — POST /collections/{collectionName}/recommendations:
const similar = await client.recommendations.getRecommendations({
collectionName: "products",
model_id: "similar",
object_id: "42",
k: 8,
exclude_ids: cartIds,
});| Field | Meaning |
|---|---|
model_id | required |
object_id / object_ids | anchor document(s); several anchors are merged |
session_id, user_id, anonymous_id | the subject, for personal models |
query | the search text, for query_to_item |
k | how many — default 10, max 100 |
filter_by | combined with the model's own filter |
exclude_ids | leave these out, e.g. items already in the cart |
include_fields, exclude_fields | shape each hit's document |
The response has model_id, hits in the same shape as search results (one
renderer serves both), took_ms, queries for recently_searched models (with
empty hits), and experiment when the request was enrolled in a running
experiment.
Close the loop
Send impressions
Send impression events for the recommendations you render, with doc_ids
and positions.
Send outcomes
Send click, add_to_cart and purchase with the same query_id.
Let event-driven models learn
Models that learn from events (related_items, bought_together,
trending, popular, session, query_to_item, sequential) improve as
events accumulate within window_days. A new catalogue can start with
looking_similar, which needs only a vector field.
Search rules and synonyms
Merchandise a collection from your server — exclusion filters, boosts, pins, redirects, diversity, zero-hit fallbacks, schedules — and teach it your vocabulary with synonym sets, stop words and stemming dictionaries.
Analytics rules and metrics
Query-performance metrics computed from your search events, plus custom analytics rules for named counters and query logs.