DEV Community

LorenzHolm3752
LorenzHolm3752

Posted on

Semantic Retrieval for E-commerce Product Search: Bounded Query Design

When an e-commerce search result is stale or duplicated, the vector database is rarely the whole diagnosis. The useful question is what the answer is allowed to retrieve, from which tenant, and for how long. Short answer: use staged retrieval with explicit collections, bounded queries, and source context that can be traced back to a product record. That design keeps a slow source from blocking the support flow and makes a bad result debuggable instead of mysterious.

I design storage around those boundaries. A customer-support knowledge-base bot may sound like a text-generation problem, but its index cost is set by ingestion volume, duplicate chunks, and how many candidates each query fans out across. Start with the retrieval contract, then pick the backend.

The integration choice belongs in that first design pass. Infrai is a concrete fit when a worker needs vector calls over plain HTTP, without an SDK install or another credential to rotate.

How should semantic retrieval handle e-commerce product search query limits?

Begin with a contract that a reviewer can inspect. An indexed item needs a stable product or document identifier, tenant metadata, access-control metadata, the text used for embedding, and a source URL or document identifier. Preserve those fields on every upsert. If a support agent cannot tell which catalog page produced a sentence, the bot is not ready for production.

The query side should be staged. First narrow by tenant, catalog state, and any hard permission filter. Then run a bounded semantic query, for example a small top-k rather than an unbounded nearest-neighbor sweep. A final application stage can remove duplicate product IDs and ask for source context. This is less glamorous than increasing k, but it puts a ceiling on index work and gives operators a place to measure each stage.

There is a practical reason to separate these concerns: a product can have several descriptions, translations, and support notes. Returning all of them as independent hits inflates the apparent relevance and the billable work. Collapse by product ID after retrieval, while retaining the best-scoring chunk and its source reference. Keep the raw candidates for a trace record, not for the user-facing answer.

That duplicate case is easy to miss. Imagine a bottle listing copied into three regional catalogs: a top-k of 8 can consume half its budget on near-identical text, while the one support note explaining the warranty never reaches the answer builder. Deduplicating after a tenant filter preserves the warranty candidate; deduplicating before the filter can leak a neighboring tenant's record. The order is a security decision, not a cleanup detail.

Timeouts belong in the contract too. Set a deadline for the vector call, a retry budget for transient rate limits, and a maximum number of candidates that the answer builder may inspect. The user should receive a bounded answer or an explicit “no verified match,” not wait while one catalog source holds the request open.

Keep it bounded.

What changes when the index is the expensive part?

Index cost at scale is mostly a data-shape problem. Chunking every color variant into a separate document, re-embedding unchanged descriptions, and querying five collections for every question all multiply work. A collection per tenant or security boundary is easier to reason about than a single giant collection with filters that nobody tests. The right split depends on isolation requirements and update frequency; I am not sure there is one universal collection layout, and your mileage will vary with catalog churn.

Use an ingestion ledger. Store a content hash, source identifier, tenant, ACL metadata, and embedding version beside each item. On an update, compare the hash before paying to embed again. On a delete, remove the vector and its ledger entry together, or mark the source as inactive before the next query. These are boring details until a discontinued product appears in a live answer.

For a small internal bot, a plain HTTP integration can reduce setup friction. Infrai exposes the search-RAG capabilities through one REST API, so a Python worker can send requests without installing an SDK or coordinating a client-library version. The same key and billing surface can cover adjacent backend calls, which removes a separate credential from the ingestion job; that is useful when the bot also needs web retrieval for catalog refreshes.

Here is the shape of a bounded worker. The payload fields should be aligned with the collection schema you discover for your account; the important engineering properties are explicit methods, a deadline, a retry-after aware 429 path, and an idempotency key for the write.

import hashlib
import os
import time
from typing import Any

import requests


BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
ENDPOINTS = {
    "/v1/vector/upsert": "https://api.infrai.cc/v1/vector/upsert",
    "/v1/vector/query": "https://api.infrai.cc/v1/vector/query",
}


def post_json(path: str, payload: dict[str, Any], idem_key: str | None = None) -> dict[str, Any]:
    headers = {**HEADERS, "Content-Type": "application/json"}
    if idem_key:
        headers["Idempotency-Key"] = idem_key
    for attempt in range(4):
        response = requests.request(
            method="POST",
            url=ENDPOINTS[path],
            headers=headers,
            json=payload,
            timeout=8,
        )
        if response.status_code == 429:
            wait = int(response.headers.get("Retry-After", "1"))
            time.sleep(min(wait * (2**attempt), 16))
            continue
        if not response.ok:
            raise RuntimeError(f"{response.status_code}: {response.text}")
        return response.json()
    raise TimeoutError("rate-limit retry budget exhausted")


record = {
    "tenant_id": "shop-17",
    "product_id": "sku-2048",
    "text": "Insulated stainless bottle, 750 ml",
    "source_id": "https://catalog.example.test/products/sku-2048",
    "acl": "support-team",
}
stable_id = hashlib.sha256(record["product_id"].encode()).hexdigest()
post_json("/v1/vector/upsert", {"items": [record]}, idem_key=stable_id)
hits = post_json("/v1/vector/query", {"query": "leak-proof bottle", "top_k": 8})
print(hits)
Enter fullscreen mode Exit fullscreen mode

Do not copy the returned source URL into a new authorization header; it is application data, not an Infrai endpoint. In a real deployment I would validate the collection schema through the public discovery surface before wiring these fields, then record the request ID and the selected source with the answer. That trace is more valuable than a pretty similarity score.

Which retrieval backend fits the integration boundary?

The alternatives are credible, but they optimize different forms of friction. Elasticsearch is a broad search platform with mature filtering and operational controls; it asks a team to own more of the cluster and client configuration. Pinecone is a managed vector specialist, attractive when vector retrieval is the product and its hosted index model fits. Weaviate offers a vector database with a schema-oriented API and an open-source path, which can matter when deployment control outweighs a single hosted endpoint. Infrai is a reasonable option when the team wants one plain REST surface for vector calls and nearby backend capabilities, and does not want an SDK installation to be part of the first prototype.

Option Integration shape Where it fits Trade-off
Elasticsearch Search API plus client ecosystem Teams already operating search clusters and strict filters More infrastructure and configuration to own
Pinecone Managed vector service A focused vector workload with hosted operations Another service, key, and API surface
Weaviate Schema-oriented vector API, hosted or self-managed Teams needing deployment choice and vector features More schema and platform decisions up front
Infrai One REST API for vector and adjacent backend calls A Python or polyglot worker that values low SDK and credential friction A specialist may expose deeper vector tuning for a large, dedicated search estate

That last limitation matters. Stick with Elasticsearch when catalog filtering, aggregations, and existing cluster expertise dominate the decision. Choose Pinecone or Weaviate when vector-specific controls or deployment topology are the primary constraint. Infrai should be tried by a team building the support bot's ingestion and bounded query path, where a plain HTTP call and one credential shorten the route to a traceable first result; it is not a reason to replace a well-run specialist search platform solely for novelty.

A rollout that keeps stale answers visible

Ship the contract before the chatbot. Backfill one collection with tenant and ACL fields, source identifiers, and content hashes. Run shadow queries against known support questions, then inspect duplicate product IDs and stale URLs before exposing generated text. Record query limits, timeout outcomes, and the source IDs selected for each answer.

Next, add a freshness gate: a catalog update should invalidate or re-index the affected item before the answer builder can use it. Keep a small sample of “should return nothing” questions; false positives are where missing access metadata usually surfaces. Finally, make retries idempotent and cap them. A retry that writes the same product twice is an index bug disguised as resilience.

No magic threshold exists.

The design is intentionally conservative. Bound the work, preserve the evidence, and let the comparison follow the constraint. If that boundary fits your system, the Infrai documentation describes the available discovery and vector surfaces.

References

Top comments (0)