Skip to content

Flow: Recommendations

Product recommendation endpoints served by the search proxy. Includes similar products, search suggestions, and personalized recommendations.

Request Path

graph TD
    A["Customer App"]

    subgraph cf["Cloudflare Worker (search_proxy: {env}-ecom-api)"]
        B["Auth & Settings middleware (same as search)"]
        subgraph rec["Recommendation handler"]
            C["Parallel context searches via MARQO_WORKER"]
            D["Marqo search suggestions API"]
            E["PIXEL_WORKER RPC for personalization data"]
        end
        F["Response with deduplicated results"]
        G["Metrics enqueue (SQS)"]
    end

    H["Customer App"]

    A --> B
    B -->|Similar / For-You| C
    B -->|Suggestions| D
    B -->|For-You| E
    C --> F
    D --> F
    E --> F
    F --> H
    F -.->|background| G

Endpoints

Endpoint Purpose Key Input
POST /api/v1/indexes/:index/recommendations/similar Products like these documentIds[] (1-10)
POST /api/v1/indexes/:index/recommendations/complementary Products that pair well documentIds[] (not yet implemented)
POST /api/v1/indexes/:index/recommendations/queries Search suggestions / autocomplete q (query prefix)
POST /api/v1/indexes/:index/recommendations/for-you Personalized recommendations userId (required)

Similar Products

Where: components/search_proxy/src/recommendations.ts

How It Works

  1. Splits documentIds into up to 6 arrays (Cloudflare concurrent connection limit)
  2. Sends parallel context searches to Marqo using HYBRID search with tensor ranking
  3. Each search uses document context (the seed products) with excludeInputDocuments: true
  4. Results interleaved and deduplicated by _id and collapseFields

Marqo request key params:

{
  "searchMethod": "HYBRID",
  "hybridParameters": { "retrievalMethod": "tensor", "rankingMethod": "tensor" },
  "context": { "documents": { "ids": { "doc_id": 1 } } }
}

Logs:

{"message": "marqo_context_search_request", "url": "..."}
{"message": "marqo_search_response", "hitsCount": 10, "processingTimeMs": 30}

Search Suggestions

Where: components/search_proxy/src/recommendations.ts

Calls Marqo's search suggestions API with:

  • q — query prefix
  • fuzzyEditDistance — typo tolerance (default 0)
  • minFuzzyMatchLength — minimum chars for fuzzy (default 3)
  • Config from search_suggestions_config in settings

Returns: { suggestions: [{ suggestion, score }] }

Personalized (For-You)

Where: components/search_proxy/src/recommendations.ts

How It Works

  1. Requires pixel_id in settings (400 error if not configured)
  2. Pixel Worker RPC: env.PIXEL_WORKER.getPersonalizationData(pixelId, userId, sessionId) — fetches tracked events (clicks, add-to-cart, purchases)
  3. Optional filter by interactionTypes (ClickEvent, AddToCartEvent, PurchaseEvent)
  4. Takes first 60 document IDs from tracked events
  5. Runs same parallel context search as similar products

Fallback: If no tracked events found → NoTrackedEventsError caught → falls back to wildcard search (q: "*") with any provided filters. Returns general products instead of failure.

Inspect: Pixel Worker is a separate Cloudflare Worker bound via service binding. Check its logs:

npx wrangler tail pixel-ingestion-worker-{env_suffix}

What to Look For

Symptom Where to Check
401 on recommendations Same auth as search — see Search.
404 "Not configured" Settings not in KV. Check Settings Sync.
Similar returns no results Seed documents exist in index? Check filter expression.
Complementary returns 400 Expected — not yet implemented.
Suggestions returning nothing Index has searchable text? Check search_suggestions_config.
For-you returning generic results No tracked events for userId (falls back to wildcard q: "*" search). Check Pixel Worker logs.
For-you returning 400 pixel_id not configured in index settings. Check Settings Sync.
Slow recommendations Multiple parallel Marqo calls. Check per-call processingTimeMs.