Scenarios
A scenario is a named placement (slug) that bundles logic type, algorithm, nextQL filters and boosters, constraints, segmentation, and optional rule references. Fetch recommendations with the scenario slug to apply the full config at request time.
Logic types (use cases)
| Setting | Type | Default | Description |
|---|---|---|---|
| user-to-item | logicType | — | Homepage “For you”, personalized rails. Requires userId. |
| item-to-item | logicType | — | PDP related (co-occurrence). Requires itemId. |
| similar-products | logicType | — | Vector similarity on PDP. Requires itemId + embeddings. |
| items-to-items | logicType | — | Cart upsell from multiple seeds. Requires itemIds. |
| recently-viewed | logicType | — | Continue browsing. Requires userId. |
Algorithm registry
algorithm selects the scoring implementation. If omitted, the default for the logic type is used. Response includes algorithm for debugging and A/B analysis.
| Setting | Type | Default | Description |
|---|---|---|---|
| weighted-history | user-to-item | default | 30d weighted interactions + popularity fallback. |
| co-occurrence | item-to-item | default | Co-interaction matrix + category fallback. |
| vector-similarity | similar-products | default | Qdrant nearest neighbors. |
| multi-seed-co-occurrence | items-to-items | default | Cart co-occurrence across seeds. |
| multi-seed-hybrid | items-to-items | — | RRF fusion of co-occurrence + vectors. |
| elsa-cf | user-to-item, item-to-item | — | Requires offline trained factors. |
| realm-sequential | user-to-item | — | Session sequential model (offline). |
| beeformer-multimodal | similar-products | — | Text + image embeddings (offline). |
List options: GET /recommendations/algorithms andGET /recommendations/algorithms/:logicType.
Scenario settings
| Setting | Type | Default | Description |
|---|---|---|---|
| slug | string | — | Stable URL-safe ID used in API and tracking. |
| filter | nextQL | — | Hard filter applied to candidates. |
| boosters | string[] | — | Score multipliers: boost by N where condition. |
| rulesSlugs | string[] | — | Reusable rules merged before scenario filter. |
| segmentationId | string | — | Diversity balancing config. |
| itemsPerSegment | number | — | Max items per segment in results. |
| minItems / maxItems | number | 6 / 12 | Auto-fill and cap on count. |
| autoRelaxConstraints | boolean | true | Relax diversity when below minItems. |
| autoRelaxFilters | boolean | false | If filter matches nothing, drop clauses right→left (opt-in). Put durable constraints on the left. |
| relaxEntireFilterOnEmpty | boolean | false | If filter auto-relax still empty, drop the whole filter (opt-in). |
| autoExpandSegments | boolean | true | Increase per-segment slots when below minItems. |
Query planning for filter auto-relax
Use when enabling autoRelaxFilters (and optionally relaxEntireFilterOnEmpty). Both default off — existing scenarios are unchanged until you opt in.
When to enable
Turn autoRelaxFilters on for merchandising filters where an empty shelf is worse than dropping soft clauses. Leave off for hard policy / compliance filters. TurnrelaxEntireFilterOnEmpty on only when showing unfiltered candidates is acceptable after soft clauses are exhausted.
Core authoring rule
Clauses drop from right to left when the full filter matches nothing. Put must-keep constraints on the left; nice-to-have on the right. Linked filter rules sit left of the scenario filter (highest rule priority first). Soft scenario clauses on the right drop before sticky rules.
AND chain
'allowed_countries' contains 'IN' and 'type' = 'game' and 'price' < 20— if nothing is under 20, price drops first; then type; countries last among these.
OR / groups
Put soft bands in the right group. Prefer clear AND + rightmost soft clauses when intent is “durable then soft.” Do not rely on OR arms as priority.
Response field (filterRelaxation)
When relaxation changes the filter, the API may return droppedClauses,effectiveFilter (null only if the entire filter was dropped),droppedEntireFilter, and originalFilter. UX idea: “Showing results without price filter.”
Contrast with constraint auto-relax
autoRelaxFilters triggers when the filter matched zero candidates.autoRelaxConstraints triggers when results are below minItems after ranking (diversity / freshness). Different knobs.
Checklist before save
(1) Left = durable; right = soft. (2) Rule priorities intentional. (3) Policy B on only if unfiltered fallback is OK. (4) Validate with Admin Try-it.
Constraints
| Setting | Type | Default | Description |
|---|---|---|---|
| maxPerCategory | number | — | Diversity cap per category in result set. |
| injectLongTail | boolean | false | Inject low-exposure items. |
| longTailPercentage | 0–100 | 10 | Share of slots for long-tail when injectLongTail true. |
| freshnessBoostDays | number | — | Boost items within N days of freshness date. |
| purchasedItemsMaxPercent | 0–100 | 10 | Cap already-purchased items; pass userId at fetch. |
Homepage diversity
maxPerCategory: 2 with segmentation by genre prevents one genre dominating the carousel.
Cart — hide already purchased
Pass userId on cart upsell fetch with purchasedItemsMaxPercent: 5.
Vector tuning (qdrantSettings)
Used by vector-similarity and hybrid algorithms.
| Setting | Type | Default | Description |
|---|---|---|---|
| score_threshold | number | — | Minimum similarity score for candidates. |
| hnsw_ef | number | — | Search accuracy vs latency tradeoff. |
| filter_categories | string[] | — | Restrict vector search to categories. |
| exact | boolean | — | Exact search mode (slower, more accurate). |
Hybrid tuning (hybridSettings)
For multi-seed-hybrid on cart-style placements.
| Setting | Type | Default | Description |
|---|---|---|---|
| strategy | rrf | centroid | adaptive | rrf | How multiple seeds fuse. |
| rrfK | number | — | RRF smoothing constant. |
| coTopK / vectorTopK | number | — | Candidate pool sizes per signal. |
| categoryBoost | number | — | Multiplier when candidate category matches a seed. |
| minCoSupport | number | — | Minimum seed support for co-occurrence candidates. |
| enabled | boolean | — | Scenario-level hybrid kill switch. |
Processing order
Algorithm candidates → rule filters → scenario filter → rule + scenario boosters → constraints → segment balancing → min/max auto-fill.
Per-request filter overrides are not supported on the unified API — tune via scenarios, rules, or multiple scenarios for preset filter combinations.
Full scenario example
POST /scenarios
{
"name": "PDP — Similar PC games",
"slug": "pdp-similar-pc",
"logicType": "similar-products",
"algorithm": "vector-similarity",
"filter": "'stock' > 0 and 'platform' in ['pc', 'multi']",
"boosters": [
"boost by 1.4 where 'featured' = true",
"boost by 1.2 where 'genre' in ['rpg', 'action']"
],
"constraints": {
"maxPerCategory": 2,
"injectLongTail": true,
"longTailPercentage": 15,
"freshnessBoostDays": 14,
"purchasedItemsMaxPercent": 10
},
"qdrantSettings": {
"score_threshold": 0.35
},
"segmentationId": "by-genre",
"itemsPerSegment": 1,
"minItems": 6,
"maxItems": 12,
"rulesSlugs": ["in-stock"],
"autoRelaxConstraints": true,
"autoExpandSegments": true
}Experiments
Attach an A/B experiment to a scenario: variants point to different scenario slugs (e.g. control vs boosted). Assignment uses userId at fetch time. See Analytics and experiments.
