You shipped semantic search. Relevance scores went up. Then support started getting tickets: customers finding the perfect product, clicking through, and discovering it is out of stock, not shipped to their country, or three times their stated budget.
This is the single most common defect we find in ecommerce vector search implementations, and it is almost always the same architectural mistake. It is worth understanding precisely, because the fix is cheap and the workarounds people reach for first are not.
The root cause: embeddings cannot represent stock
An embedding encodes semantic meaning. "Waterproof jacket for cycling in winter" maps to a region of vector space near products that are, semantically, waterproof cycling jackets for winter.
Nothing in that vector encodes:
- Whether the item is in stock right now
- Whether you ship it to the user's country
- Whether it is within their stated budget
- Whether it is eligible for their customer tier or promotion
- Whether it is discontinued but still indexed
These are transactional facts that change by the minute. Your embeddings were computed during an indexing job, possibly last night. Even if you re-embed constantly, the vector has no mechanism to express a hard boolean constraint — similarity search returns nearest, not valid.
So when the query embedding lands near an out-of-stock jacket, the index returns it with high confidence. The model is not wrong. It answered the question it was asked, which was about meaning, not availability.
The fix that seems obvious and isn't
The first instinct is to stuff constraints into the embedding — append "in stock" to the indexed text, or maintain separate indices per region.
Do not do this. It fails in ways that are annoying to debug:
- Text like
"in stock"in the document contributes weakly and inconsistently to similarity; it does not act as a filter, it acts as a mild nudge - Per-region or per-tier indices multiply your index count combinatorially and every one needs to stay in sync
- Re-embedding on every stock change is prohibitively expensive and still racy
- You cannot express ranges (price between X and Y) in an embedding at all
The constraint is boolean. Embeddings are continuous. Trying to encode one in the other is the actual bug.
The pipeline that works
Four stages, and the ordering is the entire point.
1. Retrieve broadly, from two sources. Run lexical (BM25) and vector retrieval in parallel over the same catalogue. Lexical catches exact model numbers, SKUs, and brand names that embeddings routinely fumble. Vector catches intent — "something warm for a rainy commute" — that lexical cannot touch. Over-fetch deliberately: pull 200–500 candidates, not 20, because you are about to discard a lot.
2. Fuse the two candidate sets. Reciprocal Rank Fusion is the pragmatic default — it needs no score calibration between the two systems, which is fortunate because BM25 scores and cosine similarities are not on comparable scales and normalising them properly is fiddly. Weighted fusion beats RRF only once you have relevance judgements to tune against.
3. Filter hard against transactional truth. This is the step that is usually missing or misplaced. Query your live inventory and eligibility data — not a nightly snapshot, not the search index — and drop everything unavailable, out of region, ineligible, or outside stated constraints. This is a boolean gate, not a scoring penalty. A product the customer cannot buy has no rank.
4. Re-rank what survives. Now, and only now, apply your business logic: margin weighting, inventory-position boosts, personalisation, a cross-encoder for relevance. Re-ranking a set you have already validated means every result is purchasable.
The over-fetch in step 1 exists to survive step 3. If you retrieve 20 and filter 15, you show 5 results. If you retrieve 300 and filter 240, you show a full page.
Why filtering must come after retrieval
A reasonable objection: why not pre-filter, then search only the valid subset?
You can, and some vector databases support metadata pre-filtering. It works well when the filter is broad (region, category). It degrades badly when the filter is narrow, because most ANN index structures — HNSW in particular — traverse a graph built over the full vector space. Aggressive pre-filtering makes the traversal wander through large regions of excluded nodes looking for enough valid neighbours, and both latency and recall suffer. In pathological cases you get fewer results than requested with no error.
Post-filtering with generous over-fetch is more predictable. Pre-filtering is the right call for coarse, high-cardinality-reduction filters. Use both: pre-filter on region and category, post-filter on stock and price.
The failure mode underneath the failure mode
Fix the ordering and you will find the next problem: retrieval quality is capped by catalogue quality, and most catalogues are worse than their owners believe.
Typical state: attributes populated for 40% of SKUs, three supplier vocabularies for the same colour, sizing that means different things by brand, descriptions inherited verbatim from manufacturer PDFs. Semantic search over that produces semantically accurate matches to text that does not describe the product well.
This is now tractable in a way it was not two years ago. Vision-language models extract structured attributes from product photography — material, closure type, pattern, fit — at a per-SKU cost that makes enriching a large catalogue a real budget line. Two guardrails from experience:
- Write model output to a staging layer with a confidence score, never directly to the master catalogue. Auto-promote high confidence, queue the rest for human review.
- Measure by downstream effect, not coverage. "94% of attributes populated" tells you nothing. "Null-result rate fell, filter-refined sessions rose" tells you it worked.
Instrumentation worth adding today
Whatever else you do, log these — they make every future debate empirical:
- Null-result rate by category (the clearest catalogue-quality signal you have)
- Post-filter result count distribution (if the p10 is near zero, your over-fetch is too small)
- Click-through by retrieval source — lexical, vector, or both — which tells you whether hybrid is earning its complexity
- Zero-result queries, verbatim, reviewed weekly by someone from merchandising
One more thing: agents are querying you too
Worth knowing while you are in this code. A growing share of ecommerce traffic comes from AI shopping agents, and Adobe measured AI-referred retail traffic up 393% year over year in Q1 2026, converting substantially better than traditional search.
Agents do not use your search UI. They consume feeds and APIs. But they are unforgiving about exactly the things this pipeline gets right or wrong: if your API returns products that turn out to be unavailable at checkout, an agent learns to discount you. Same correctness requirement, different consumer, higher stakes — because an agent's disappointment is systematic rather than one shopper's bad afternoon.
Full architecture, cost ranges, and rollout sequencing: AI in Ecommerce: What Actually Drives Revenue in 2026.
Frequently Asked Questions
Should I use hybrid search or is vector search enough?
Hybrid, for any real catalogue. Pure vector search reliably fails on exact identifiers — SKUs, model numbers, specific brand names — which are a meaningful share of commercial queries and the ones with the highest purchase intent.
How many candidates should I over-fetch before filtering?
Start at 10–20× your display count and tune from the post-filter count distribution. If the tenth percentile of surviving results drops below a full page, increase it. It is a cheap parameter to get right empirically.
Is RRF better than weighted score fusion?
RRF is the better default because it requires no score normalisation between systems. Weighted fusion outperforms it once you have labelled relevance data to tune weights against — which most teams do not have at the start.
Can I filter inside the vector database instead?
For coarse filters like region or category, yes — pre-filtering is efficient there. For stock and price, post-filter against live data. Narrow pre-filters degrade ANN recall and latency in ways that are hard to detect until users complain.
How often should I re-index embeddings?
On content change, not on transactional change. Stock and price should never trigger re-embedding — they belong in the filter step, which reads live data and needs no index update at all.
What is the fastest way to improve search relevance?
Usually catalogue enrichment, not model changes. Retrieval quality is bounded by how well your product text and attributes describe your products, and most teams are leaving far more on the table there than in their choice of embedding model.
TechCirkle builds search, discovery, and agent-ready commerce APIs. More on AI development services or talk to our team.



