
The problem
Bought-together recommendations are the load-bearing cross-sell pattern in e-commerce. Show the buyer of dog food the dental treats; show the buyer of running shoes the moisture-wicking socks; show the buyer of the SaaS subscription the integration add-ons. The cross-sell decision has been made millions of times across e-commerce history; what is missing in most catalog tools is a system that finds the actual statistical co-occurrence in the data rather than relying on hand-curated cross-sell rules.
Hand-curated rules are fragile. They reflect the merchandiser's best guess at launch; they do not update as buying patterns shift; they miss the long-tail patterns no one thought to encode. The data has the answer — millions of orders show which products actually co-occur — but the data has been silent because there is no easy way to query the answer.
How it works
The demo answers the question at two levels. Patterns › Baskets runs _relate over orders for an anchor category and ranks the categories that share its orders by lift: of the orders holding the anchor, how much more often do they also hold the candidate than orders overall? In the Shop, a product's drawer runs the same _relate over the orders' SKUs and lists the products often in the same basket. Product pairs are thin (a few orders each), so a pair is shown only with at least five orders and a 95 % interval above 1, with its count beside it.
The dog dry food and dental treats result, 3.17× in the generated PetNord data, is not a curated rule. It is a live conditional probability over the actual co-purchase data. Run the query, get the number. Change the data (more orders accumulate, seasonal patterns shift, a new product enters the catalog), and the next query returns the updated number — no batch recommendation rebuild, no separate model.
{
"from": "orders",
"where": {
"skus": {
"$match": "SKU-PT-0003"
}
},
"relate": "skus",
"limit": 40
}For the full architecture, see the technology overview. For the broader narrative across multiple use cases, read The Predictive Application.
See it live
This use case runs in the 🛒 E-commerce demo today. Click through to the live application and inspect the queries that produce the result.
Frequently asked
How does this compare to collaborative-filtering recommendation engines (matrix factorization, neural CF)?
Collaborative filtering builds a user-item matrix and decomposes it. Effective on dense data; struggles on cold-start items and tail products. _relate over co-purchase data is a simpler statistical primitive that returns conditional lift directly, with the counts behind it, so every pair is explainable and reflects the current orders without a rebuild.
What about user-personalized cross-sell (different recommendations per user)?
Personalized cross-sell is _recommend against the impressions or clicks data conditioned on the user's segment or behavior. Bought-together via _relate is the catalog-level pattern; personalized cross-sell composes that with user-conditional probability. Both run from the same data, no separate model.
How do we avoid the obvious-bundle problem (recommending things customers always buy together anyway)?
The lift metric handles this. Lift = conditional / baseline. A product pair that is bought together a lot but also bought separately a lot has low lift — the recommendation is not surprising. A pair with high lift is statistically over-represented among co-purchases; that is the recommendation worth surfacing.
Does this surface seasonality (e.g., dog dental treats at vet-visit time)?
_relate is computed over the current data at query time. For explicit seasonality, add a month or season filter to the where clause; the demo's Patterns view does this for categories in its "who buys what, and when" section.
Will this scale to a catalog with millions of SKUs?
The columnar index makes _relate scale with the data volume, not the catalog size. In a large catalog, product-level pairs get thinner, which is why the demo shows category pairs on Patterns and keeps a product pair only when its count and interval hold up.


