AI infrastructure

3,072-dimension embeddings: the maths under GoGee's product search

Semantic search is only as good as its vector layer, and vector layers are where most implementations quietly cut corners, smaller models, truncated dimensions, no index, or a similarity threshold set so low that everything matches everything.

This article documents exactly how GoGee's is built, because it is the shared foundation for text search, photo search and voice search alike.

GoGee feature series · 4 of 33

How it's actually built

Embedding model
google/gemini-embedding-001
Dimensions
3,072 native (no truncation)
Storage type
halfvec(3072) on the product row
Index
HNSW, halfvec cosine operators
Distance
Cosine, exposed as 1 − distance similarity
Default floor
0.15, raised to 0.25–0.35 for stricter queries
Embedding pipeline
  1. 01Input

    Product record

    Name, attributes and imagery

  2. 02AI

    Embed

    gemini-embedding-001 at 3,072 native dimensions

  3. 03Database

    Store

    halfvec(3072) column on the product row

  4. 04Database

    Index

    HNSW with halfvec cosine operators

  5. 05Output

    Similarity lookup

    Cosine distance exposed as 1 − distance

Guardrails and fallbacks

  • 0.15 default floor
  • 0.25–0.35 for stricter queries
  • No dimension truncation

3,072-dim product embeddings, data flow, generated from the shared GoGee feature diagram template.

Why 3,072 dimensions, stored as halfvec

The catalogue started on a 1,536-dimension vector column and was migrated to 3,072 dimensions using the half-precision vector type. Higher dimensionality preserves more nuance between products that differ in one attribute, a 5-litre versus 10-litre version of the same item, while half-precision storage keeps the index small enough to stay fast.

The index is HNSW with cosine operators, which is what allows an approximate nearest-neighbour lookup across thousands of SKUs to return inside a page load rather than a spinner.

What actually gets embedded

A product is embedded from a composed text blob rather than its name alone: product name, AI-enhanced name, short and long descriptions, supplier attributes, colours, materials and styles, category names, custom fields, variant labels and SKU. Where product photos have been captioned by the vision model, that caption is included as a visual description line.

This is the single biggest lever on search quality. Thin product data produces thin vectors, and no model choice compensates for a catalogue where half the products have a name and nothing else. It is also why the catalogue-content tooling and the search tooling are sold as one platform rather than two.

The similarity function is a database function

Matching happens in Postgres through a similarity RPC that accepts the query vector, a match count, a minimum similarity, and optional category, brand and price-range filters. It returns products with a similarity value computed as one minus cosine distance.

Because filtering happens inside the same query as the vector match, a price bound or category restriction narrows the candidate set at the database level instead of being applied to an already-truncated result page, which is the usual reason vector search appears to "ignore" filters.

  • Threshold is per-call: looser for photo search, stricter for feature-specific queries
  • Refinement passes can drop the floor to zero and re-rank within a known set
  • Category, brand and price filters are arguments to the match function itself

Embeddings are generated in controlled batches

Vectors are produced by an admin-triggered batch job that selects candidate products, builds the text blob, embeds them and writes the vector plus a timestamp back to the product row. Because the timestamp is stored, re-embedding can target only products whose content changed since they were last embedded.

That is deliberate rather than lazy: a database trigger firing an AI call on every product edit is how clients end up with unpredictable AI bills during a bulk import of 40,000 rows.

Questions we get asked

Do you re-embed the whole catalogue every time?

No. Each product stores when it was last embedded, so batches can target only new or changed products.

Is this a separate vector database?

No. Vectors live in the same Postgres database as your products, which is why filters, stock and pricing can be applied in the same query as the similarity match.

Does image search use image embeddings?

Not at query time. Photos are captioned by a vision model and the caption is embedded with the same text-embedding model, so both search modes share one index.