RecomNext

Events & analytics

Impressions, interactions, weights, and CTR.

03 - Events, weights & analytics

Related: Case study - Driffle CTR · Appendices SQL

Event types

Interactions (recomnext.interactions)

typeTypical meaningDefault event weight
viewProduct/page engagement (SDK trackView, widget card click often sends view)1.0
cartAdd to cart (trackCart)1.0
purchasePurchase (trackPurchase)1.0
ratingExplicit rating (if integrated)1.0

Important fields:

FieldRole
tenant_idIsolation
user_idActor (may be sess_… anonymous)
item_idCatalog externalId
session_idOptional
typeview/cart/purchase/rating
weightPer-event multiplier (usually 1)
scenario_slugAttribution to a scenario (empty = site-wide / unattributed)
event_timeWhen it happened

Impressions (recomnext.impressions)

One row per item shown (API accepts itemIds[]; engine expands to rows).

FieldRole
tenant_id, user_id, item_id, session_idSame ideas as interactions
scenario_slugPlacement attribution (required for scenario funnel analytics)
event_timeWhen shown

Impressions are usually fired by IntersectionObserver when a rail card is ≥ ~50% visible.


How events are stored (path)

  • Writes are async (202 accepted → Kafka → CH). Slight delay before metrics update is normal.
  • Behavioral recommenders read CH with windows like 30 days (purchase cap uses 90 days).

Weights - two layers

1) Event weight field

Optional float on each interaction (default 1.0). Rarely changed by hosts today.

2) Type multipliers (weighted-history algorithm)

When scoring a user’s history, the engine computes:

sum(
  weight * multiIf(
    type = 'purchase', 4,
    type = 'cart',     2,
    type = 'rating',   3,
    1                  -- view and anything else
  )
) AS total_weight

…over the last 30 days, grouped by item_id, for that user_id.

TypeMultiplier
purchase×4
rating×3
cart×2
view (default)×1

These multipliers are code defaults for weighted-history. They are not Admin scenario toggles today. Changing them requires an engine change.

Worked micro-example

User U in 30d:

ItemEvents
A3 views (weight 1)
B1 cart + 1 view
C1 purchase

Scores:

  • A: 3 × 1 × 1 = 3
  • B: 1×2 + 1×1 = 3
  • C: 1 × 4 = 4 → ranks first

Popular fallback (cold start): if the user has too few personal items, the algo also pulls 7-day global popular items (by event count) with score 0.5 when they lack personal weight.


Analytics formulas (Admin scenario summary)

For scenario slug X, last N days (default 30):

MetricDefinition
Impressionscount() from impressions where scenario_slug = X
Clickscount() from interactions where scenario_slug = X and type = 'view'
Cartssame table, type = 'cart'
Purchasessame table, type = 'purchase'
CTR %round(10000 * clicks / impressions) / 100 if impressions > 0
Cart rate %carts / impressions
Conversion %purchases / impressions

Dev snapshot (driffle-dev, ~30d, as of 2026-07-23)

Interaction typeCountAvg event weight
view114,5541.0
cart171.0
purchase131.0

For scenario pdp-similar-products:

Impressions“Clicks” (view+slug)CTR
48,696114,385234.9%

Why >100% is possible: 09 - Case study.


Attribution rules of thumb

DoDon’t
Send impressions with scenarioSlug when cards are shownAssume widget click alone creates scenario CTR
Send trackView / cart / purchase with the same slug only when the action is attributable to that placementStamp every PDP view with the PDP scenario slug
Keep stable userId / merge identity after loginCompare CTR across scenarios with different instrumentation

Site-wide history (empty scenario_slug) still feeds behavioral algos; it simply does not appear in per-scenario funnel endpoints.


Identity

  • Anonymous browse often uses sess_… ids.
  • After login, mergeIdentity(authenticatedUserId) merges session history into the user.
  • Broken merge → fragmented history → weaker user-to-item / purchase-cap behavior.