Every third visitor to an online store leaves without buying due to irrelevant recommendations. Worse, showing the same types of products reduces trust in the platform. On one project — an electronics store with 50,000 SKUs — we found that the "similar products" block based on categories returned nearly identical items: 8 out of 8 were smartphones of the same brand. Click-through rate dropped to 0.3%. The problem isn't lack of data, but algorithm choice. Content-based on attributes gives narrow results, collaborative filtering suffers from cold start, and a hybrid requires a proper mix. With over 5 years of experience in e-commerce, we have implemented recommendation systems for 15+ stores (turnover from 10 million RUB/month) and found the balance: average conversion increase of 18%, and for one client, +34% by introducing association rules "frequently bought together." E-commerce personalization is not just a trend — it's a necessity for customer retention. Our certified team guarantees tailored solutions for conversion rate optimization. This typically results in $5,000–$10,000 monthly revenue lift for stores with 1000 daily orders.
Which recommendation algorithm to choose?
The choice depends on data and goals. Content-based searches by attributes — quick to launch, but gives homogeneous results. Collaborative filtering (ALS) uncovers hidden patterns — more accurate, but requires behavior history. The hybrid approach combines the best of both: 25% more clicks than pure content-based. For a new store without history, we recommend starting with content-based on embeddings, gradually adding ALS as data accumulates.
| Algorithm | Required Data | Advantages | Disadvantages | Time to Implement |
|---|---|---|---|---|
| Content-based (embeddings) | Product descriptions | Works immediately, no history needed | Homogeneous results | 2–3 days |
| Collaborative filtering (ALS) | User behavior | High accuracy, hidden patterns | Cold start for new users | 1–2 weeks |
| Hybrid recommendations | Both types | Best CTR (20%+), smooths cold start | Complex weight tuning | 2–3 weeks |
Why is the hybrid approach more effective?
It doesn't suffer from cold start: new products get recommendations via embeddings, older ones via user behavior. Diversity is higher — the block isn't filled with identical items. We use dynamic mixing: if the user has little data, we emphasize content-based, and vice versa. In practice, this gives a 15–20% CTR increase compared to homogeneous output. Additionally, the hybrid can increase average order value by 12–15% through cross-sells. For a typical store with 1000 daily orders, this translates to $5,000–$10,000 monthly revenue lift.
Data structure and indexing
For embeddings, we collect a textual representation of the product:
function buildProductText(product) {
return [
product.name,
product.brand,
product.category + ' > ' + product.subcategory,
product.description?.slice(0, 500),
product.tags?.join(', '),
Object.entries(product.attributes || {})
.map(([k, v]) => `${k}: ${v}`)
.join(', '),
].filter(Boolean).join('\n');
}
async function indexProduct(product) {
if (!product.active || product.stock === 0) return;
const text = buildProductText(product);
const { data: [{ embedding }] } = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text,
});
await db.query(`
INSERT INTO product_embeddings (product_id, embedding, updated_at)
VALUES ($1, $2::vector, NOW())
ON CONFLICT (product_id) DO UPDATE
SET embedding = $2::vector, updated_at = NOW()
`, [product.id, JSON.stringify(embedding)]);
}
Indexing runs on every product update. Embeddings are stored in pgvector — search speed <10ms for 100K products.
Finding similar products
async function getSimilarProducts(productId, options = {}) {
const { limit = 8, minPrice, maxPrice, inStockOnly = true } = options;
const result = await db.query(`
WITH source AS (
SELECT pe.embedding, p.price, p.category_id
FROM product_embeddings pe
JOIN products p ON p.id = pe.product_id
WHERE pe.product_id = $1
)
SELECT p.id, p.name, p.slug, p.price, p.main_image, p.rating, p.reviews_count,
1 - (pe.embedding <=> source.embedding) AS similarity
FROM product_embeddings pe
JOIN products p ON p.id = pe.product_id
CROSS JOIN source
WHERE pe.product_id != $1 AND p.active = true
AND ($2::boolean IS FALSE OR p.stock > 0)
AND ($3::numeric IS NULL OR p.price >= $3)
AND ($4::numeric IS NULL OR p.price <= $4)
ORDER BY pe.embedding <=> source.embedding
LIMIT $5
`, [productId, inStockOnly, minPrice || null, maxPrice || null, limit]);
return result.rows;
}
We added filters for price and stock — results are always relevant and meet business rules.
Association rules: "frequently bought together"
We analyze order history from the last 90 days via FP-Growth (faster than Apriori on large data):
from mlxtend.frequent_patterns import fpgrowth, association_rules
import pandas as pd
def compute_frequently_bought_together():
orders = fetch_orders_last_90_days()
basket = orders.groupby(['order_id', 'product_id'])['product_id'] \
.count().unstack().fillna(0)
basket = basket.map(lambda x: 1 if x > 0 else 0)
frequent_sets = fpgrowth(basket, min_support=0.005, use_colnames=True)
rules = association_rules(frequent_sets, metric='lift', min_threshold=1.5)
for _, rule in rules.iterrows():
antecedent = list(rule['antecedents'])[0]
consequent = list(rule['consequents'])[0]
save_association(antecedent, consequent, rule['confidence'], rule['lift'])
Practical benefit: when a smartphone is added to the cart, we suggest a case and screen protector — upsell conversion increases by 40%. For a clothing store, we found buyers of jeans often buy a belt — this increased average order value by 12%. Boosting loyalty through relevant offers reduces retargeting costs by 20–25%, saving up to $5,000 per month in ad spend.
Personalized recommendations with ALS
We use implicit.ALS (64 factors, 30 iterations) on a behavioral matrix (view=1, cart=3, purchase=10):
import implicit
from scipy.sparse import csr_matrix
def train_product_model(events):
users_idx = {u: i for i, u in enumerate(events['user_id'].unique())}
items_idx = {p: i for i, p in enumerate(events['product_id'].unique())}
matrix = csr_matrix((
events['weight'],
(events['user_id'].map(users_idx), events['product_id'].map(items_idx))
))
model = implicit.als.AlternatingLeastSquares(factors=64, iterations=30)
model.fit(matrix.T)
return model, users_idx, items_idx
For each user, we get the top-N items and mix with content-based for diversity.
Recommendation diversity
A block of 8 identical laptops degrades UX. A diversification mechanism re-ranks output, penalizing similar categories:
function diversify(recommendations, diversityFactor = 0.3) {
const selected = [recommendations[0]];
const remaining = recommendations.slice(1);
while (selected.length < 8 && remaining.length > 0) {
const scores = remaining.map(candidate => {
const maxSimilarity = Math.max(
...selected.map(s => categorySimilarity(s, candidate))
);
return { item: candidate, score: candidate.score * (1 - diversityFactor * maxSimilarity) };
});
scores.sort((a, b) => b.score - a.score);
selected.push(scores[0].item);
remaining.splice(remaining.indexOf(scores[0].item), 1);
}
return selected;
}
Result: output includes products from different categories and price segments. A/B testing of recommendations showed diverse output increases CTR by 22% without sacrificing conversion.
How is diversity tuned in practice?
You can add popularity or margin weights to balance relevance and business metrics.Implementation process
- Analysis — examine assortment, behavior scenarios, data.
- Design — select algorithms for business tasks.
- Prototype — run on real data, measure quality.
- A/B test — compare with current version (or lack thereof).
- Deploy and monitor — set up dashboards, record baseline.
What's included
- Architecture documentation and model descriptions
- Team training on working with the system
- Dashboard of key metrics (CTR, conversion, revenue)
- Support for one month after launch
Estimated timeline and cost
| Component | Timeline | Cost (USD) |
|---|---|---|
| Content-based similar products (pgvector) | 3–4 days | $2,000–$3,000 |
| Association rules (frequently bought together) | +2–3 days | +$1,500–$2,000 |
| Personalized recommendations (ALS) – Python service | +4–5 days | +$3,000–$5,000 |
| Full system + A/B + analytics | 3–4 weeks | $8,000–$12,000 |
Cost is calculated individually after analyzing your project. Get a consultation from an engineer — we will prepare a commercial proposal with precise timelines and stages. Contact us to discuss details and start increasing your store's conversion.







