Stage 4 — Canonical
2.7 Stage 4 — Canonical product matching
This stage collapses different surface forms of the same product into a single canonical identifier. For example:
COCA COLA 330ML KUTUC.COLA 33CL TENEKECOCA-COLA 0.33 LCOKA 330 ML
All four resolve to the same canonical_product_id. This resolution is a precondition for price memory and the B2B data product.
Approach
Canonical resolution runs asynchronously in a background post-process worker, after the synchronous flow has already returned the verified preview to the user. This keeps product resolution off the latency-sensitive path: the user sees their receipt immediately, while canonical identifiers attach to the record moments later.
The resolver works on an alias table that maps raw receipt-line text to canonical products:
- Fuzzy alias lookup — the normalized line text is matched against previously learned receipt aliases using PostgreSQL trigram similarity (
pg_trgm). A hit resolves directly to the canonical product. Aliases learned at one merchant are reused across merchants only when the text reads as a real product name rather than a store-private abbreviation, which keeps distinct products from merging under one canonical. - LLM fallback — on a miss, a language model normalizes the raw text into brand, product, and size attributes. The result is upserted as a new canonical product (or mapped to an existing one) together with a new alias row, so the same surface form resolves without a model call next time.
The similarity settings and the normalization prompt are managed in the internal operations layer.
An unresolved line item is recorded with a null canonical reference; resolution can complete on a later pass as the alias table grows.
Taxonomy structure
category > subcategory > brand > product > variantExample:
Beverages > Carbonated Soft Drinks > Coca-Cola > Coca-Cola Classic > 330 ml canEach canonical product carries normalised attributes: size_value, size_unit, package_type, brand_id, is_private_label, barcode_gtin (when available).
Growth model
The canonical index grows from receipt traffic itself: every LLM-normalized line adds a canonical product and an alias, and every repeat of that surface form afterwards resolves from the alias table without model cost. Ambiguous or low-quality entries are reviewed through the admin catalog tooling.