Brinicle

E-Commerce Product Search

Build fast, relevant product search with Brinicle ItemSearchEngine

Overview

E-commerce platforms need search that understands both structured metadata (brand, category, price) and semantic intent (what the user actually means). Brinicle's ItemSearchEngine handles both in a single HNSW index, eliminating the need for separate lexical and vector search systems.

This is achieved through alpha-controlled scoring: structured lexical signals and optional semantic vectors are encoded into one numeric representation and searched through the same HNSW graph.

Setup

Lexical-Only Product Catalog

For pure structured search without embeddings:

import brinicle

engine = brinicle.ItemSearchEngine(
    "products_index",
    dim=96,
    alpha=0.0,  # lexical-only
)

engine.init(mode="build")

products = [
    {
        "id": "p1",
        "title": "Apple iPhone 15 Pro Max 256GB Natural Titanium",
        "category": "Electronics",
        "subcategory": "Smartphones",
        "attributes": {"brand": "Apple", "storage": "256GB", "color": "Natural Titanium"},
    },
    {
        "id": "p2",
        "title": "Samsung Galaxy S24 Ultra 512GB Black",
        "category": "Electronics",
        "subcategory": "Smartphones",
        "attributes": {"brand": "Samsung", "storage": "512GB", "color": "Black"},
    },
    {
        "id": "p3",
        "title": "Sony WH-1000XM5 Wireless Noise Cancelling Headphones",
        "category": "Electronics",
        "subcategory": "Headphones",
        "attributes": {"brand": "Sony", "type": "Over-ear", "feature": "Noise Cancelling"},
    },
]

for p in products:
    engine.ingest(
        external_id=p["id"],
        title=p["title"],
        category=p["category"],
        subcategory=p["subcategory"],
        attributes=p["attributes"],
    )

engine.finalize()

Hybrid Product Search with Embeddings

For combining structured filters with semantic understanding:

import numpy as np
import brinicle

VECTOR_DIM = 384

engine = brinicle.ItemSearchEngine(
    "hybrid_products_index",
    dim=96,
    vector_dim=VECTOR_DIM,
    alpha=0.95,
    vector_normalized=True,
    M=48,
    ef_construction=1024,
    ef_search=512,
)

engine.init(mode="build")

for p in products:
    engine.ingest(
        external_id=p["id"],
        title=p["title"],
        category=p["category"],
        subcategory=p["subcategory"],
        attributes=p["attributes"],
        vector=np.random.randn(VECTOR_DIM).astype("float32"),
        normalize=True,
    )

engine.finalize()

Searching

results = engine.search("iphone 15 pro max", k=10)

Filtered Search with Metadata

results = engine.search(
    "smartphone",
    category="Electronics",
    subcategory="Smartphones",
    attributes={"brand": "Apple"},
    k=10,
)

Semantic Search with Vectors

query_vector = embed("best phone for photography")  # your embedding function

results = engine.search(
    "best phone for photography",
    vector=query_vector,
    normalize=True,
    k=10,
)

Add typeahead suggestions using AutocompleteEngine:

ac = brinicle.AutocompleteEngine("suggestions_index", dim=48)

ac.init(mode="build")

ac.ingest("iphone 15 pro max", "iphone 15 pro max")
ac.ingest("iphone 15 case", "iphone 15 case")
ac.ingest("samsung galaxy s24", "samsung galaxy s24")
ac.ingest("wireless headphones", "wireless headphones")
ac.ingest("noise cancelling earbuds", "noise cancelling earbuds")

ac.finalize()

# As user types "iph"
suggestions = ac.search("iph", k=5)

Performance

On 1.2 Million Amazon products, Brinicle achieved sub-millisecond p99 latency and 1,731 MB peak search memory, the lowest among Brinicle, Meilisearch, OpenSearch, Typesense, and Weaviate. It also achieved the best Hit@1 and nDCG@10 in that benchmark.

On this page