E-commerce Product Recommendations
5,000 products, typed BOUGHT_WITH edges, price-filtered in one call
What if 'also bought' edges and semantic search lived in one engine, with the price filter in the same call?
Zentara Pro Waterproof Outdoor Speaker
$380.42, Electronics. A Bluetooth speaker with outdoor, portable, waterproof positioning.
Its top 'also bought' result is a Midi Wrap Dress, surfaced by a real BOUGHT_WITH edge (customers bought them together), then ranked by description relevance. The co-purchase edges span all 8 categories.
Customers Also Bought
We asked SwarnDB: what was genuinely bought with this speaker? A hybrid query seeded by description, walked the BOUGHT_WITH edges, and ranked by relevance, returning 20 products in 0.6ms. The top result is a dress, surfaced because customers actually bought them together, then ranked by description relevance. Only 2 of 20 are electronics; the rest span 7 categories.
Key insight:The top 'also bought' is a dress, by real co-purchase, not a word overlap. Auditable back to the orders.
| # | Product | Category | Edge |
|---|---|---|---|
| 1 | Vossmark Signature Midi Wrap Dress | Clothing | BOUGHT_WITH |
| 2-5 | Various cross-category products | Mixed | BOUGHT_WITH |
Cross-Category Co-Purchase
From one electronics product, the BOUGHT_WITH edges reach all 8 categories, because customers genuinely cross-shop and the order history records it. Clothing leads. This is not a word overlap; it is recorded behavior, with provenance behind every edge.
Key insight:Co-purchase edges from one product span all 8 categories. Clothing leads, by behavior, not by description.
| Category | Edges | Why |
|---|---|---|
| Clothing | 19 | Frequently bought together |
| Electronics | 17 | Same domain, co-purchased |
| Sports & Outdoors | 3 | Outdoor activity bundles |
| Home & Kitchen | 3 | Everyday-use bundles |
| Food & Gourmet | 3 | Adventure lifestyle baskets |
| Beauty & Personal Care | 2 | Premium lifestyle baskets |
| Toys & Games | 2 | Entertainment bundles |
| Books | 1 | Adventure interest overlap |
Co-Purchase Network Expansion
From one speaker, we walked the BOUGHT_WITH edges outward. One product. Three hops. 500 products across all 8 categories. The co-purchase graph is richly interconnected because real baskets cross categories.
Key insight:From ONE product, 3 hops over BOUGHT_WITH edges reach the catalog across all 8 categories.
| Depth | Products | Categories | Time |
|---|---|---|---|
| 1 hop | 50 | all 8 | 0.8ms |
| 2 hops | 354 | all 8 | 1.1ms |
| 3 hops | 500 | all 8 | 2.3ms |
Bulk Edge Import
Co-purchase and co-view edges are derived from order and session logs, then bulk-imported as CSV or JSONL in one call. Every edge knows its type, confidence, and source events. No standing ETL job to keep a separate co-purchase graph aligned with the catalog.
Key insight:Millions of authored co-purchase edges, bulk-imported with provenance. No second store to sync.
| Edge Type | Derived From | Carries |
|---|---|---|
| BOUGHT_WITH | order history | co-purchase count, provenance |
| VIEWED_WITH | session logs | co-view count, provenance |
Price-Filtered Recommendations
Filter-then-search fixes the in-budget set first ($20-$50), then ranks within it, returning the true top-k among qualified products. Combined with the co-purchase traversal, one call does the whole job. Traditional stacks need a search service, a price filter service, and application stitching. SwarnDB does it in 7.4ms.
Key insight:Filter-then-search returns the true top-k among in-budget products. One call, one round trip, 7.4ms.
Meet the Yoga Mat
Different product, different category. The Trailmark Summit Travel Yoga Mat ($22.49, Sports). One composable query: seed by description, traverse BOUGHT_WITH, rank by relevance. Result: 200 products across all 8 categories in 2.7ms, every one auditable to the orders behind its edge.
Key insight:From a $22 yoga mat, one hybrid query built a complete 'You Might Also Like' feed across all 8 categories.
Search + Graph: One Query
The hybrid query returns 10 results that were both genuinely co-purchased with the seed and rank well by description, in 1.7ms. Plain vector search returns look-alikes with no behavioral signal. The graph traversal adds the 'also bought' structure, ranked by meaning, in one query.
What Would This Take Without SwarnDB?
A side-by-side look at the traditional approach versus SwarnDB.
| Capability | Traditional Stack | SwarnDB |
|---|---|---|
| Storage | Postgres + ML store + graph store | One database, one object per product |
| 'Also bought' | Opaque ML output | Typed BOUGHT_WITH edges, with provenance |
| Cross-category | Manual merchandising rules | Real co-purchase edges |
| Price-filtered recs | Search + filter services + stitching | Filter-then-search, one call |
| Edge ingest | Standing ETL sync job | Bulk import CSV / JSONL |
| Search + relationships | Multiple queries, app-level joins | One hybrid query, 1.7ms |
The Numbers
End-to-end performance from the benchmark run.
5,000 products loaded into SwarnDB
283 vec/sec
Customers Also Bought (20 products)
Cross-category co-purchase reach
Network: 500 products across 8 categories
3 hops
Price-filtered recs ($20-$50)
one call
Full query (yoga mat, 200 products)
Hybrid query (10 results)
The Code
Everything above, in a few lines of Python.
from swarndb import SwarnDBClient
from swarndb.search import Filter
client = SwarnDBClient(host="localhost", port=50051)
# "Also bought" is a typed edge derived from orders, with provenance.
client.graph.put_edge("products", source=speaker_id, target=dress_id,
edge_type="BOUGHT_WITH",
provenance={"source": "orders", "co_count": 312})
client.graph.bulk_import_edges("products", copurchase_rows, format="csv")
# Price-filtered recommendations: scope by structure, rank by meaning (7.4ms)
recs = (
client.graph.query("products")
.vector_similar(speaker_embedding, k=50)
.traverse("BOUGHT_WITH", direction="outgoing")
.vector_rank(speaker_embedding, k=10)
.return_nodes()
)
# Filter-then-search: the true top-k among in-budget products
filtered = client.search.query("products",
vector=speaker_embedding, k=10,
filter=Filter.between("price", 20.0, 50.0)
)