[object Object]

← back to Dw Photo Capture

visual-search /similar: also return storefront handle (LEFT JOIN shopify_products) so PDP cards link to real product pages

06f58395cfacea2dbeaaffd449db896c6c33cdc5 · 2026-07-08 14:55:13 -0700 · Steve

Files touched

Diff

commit 06f58395cfacea2dbeaaffd449db896c6c33cdc5
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 8 14:55:13 2026 -0700

    visual-search /similar: also return storefront handle (LEFT JOIN shopify_products) so PDP cards link to real product pages
---
 visual-search/search_service.py | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/visual-search/search_service.py b/visual-search/search_service.py
index 1cd5d32..127f1cc 100644
--- a/visual-search/search_service.py
+++ b/visual-search/search_service.py
@@ -87,18 +87,20 @@ _FAM_ADJ = {
 
 def load():
     conn = psycopg2.connect(DB); cur = conn.cursor()
-    # LEFT JOIN vendor_catalog for the color/style re-rank signals (dominant_color_hex + ai_styles).
-    # Keyed by dw_sku; rows without a match just carry null fam/styles (soft signals, never required).
+    # LEFT JOIN vendor_catalog for the color/style re-rank signals (dominant_color_hex + ai_styles),
+    # and shopify_products for the storefront `handle` so /similar can return a real PDP link.
+    # Keyed by dw_sku; rows without a match just carry null fam/styles/handle (soft signals, never required).
     cur.execute("""select e.vc_id, e.dw_sku, e.mfr_sku, e.vendor_code, e.pattern_name, e.image_url,
-                          e.embedding, v.dominant_color_hex, v.ai_styles
+                          e.embedding, v.dominant_color_hex, v.ai_styles, sp.handle
                    from image_embeddings e
                    left join vendor_catalog v on v.dw_sku = e.dw_sku
+                   left join shopify_products sp on sp.dw_sku = e.dw_sku
                    where e.embedding is not null""")
     vecs, meta, fam, styles, sku_ix = [], [], [], [], {}
-    for vc_id, dw_sku, mfr, vc, pat, url, emb, hexc, ai_styles in cur:
+    for vc_id, dw_sku, mfr, vc, pat, url, emb, hexc, ai_styles, handle in cur:
         i = len(meta)
         vecs.append(np.frombuffer(bytes(emb), dtype="float32"))
-        meta.append({"vc_id": vc_id, "dw_sku": dw_sku, "mfr_sku": mfr, "vendor": vc, "pattern": pat, "image": url})
+        meta.append({"vc_id": vc_id, "dw_sku": dw_sku, "mfr_sku": mfr, "vendor": vc, "pattern": pat, "image": url, "handle": handle})
         fam.append(_family_from_hex(hexc))
         # ai_styles is jsonb array → normalize to a lowercase set for overlap test
         sset = set()
@@ -217,7 +219,7 @@ class H(BaseHTTPRequestHandler):
             for _rank, base, i in scored[:k]:
                 m = meta[i]
                 res.append({"dw_sku": m["dw_sku"], "image": m["image"], "pattern": m["pattern"],
-                            "vendor": m["vendor"], "score": round(base, 4)})
+                            "vendor": m["vendor"], "handle": m.get("handle"), "score": round(base, 4)})
             self._send(200, {"ok": True, "n": STATE["n"], "query": {
                 "dw_sku": dw_sku, "family": qfam, "styles": sorted(qstyles)}, "results": res})
         except Exception as e:

← 3e6d84f Save EVERY scan (Steve): identify-multi now persists the raw  ·  back to Dw Photo Capture  ·  dwphoto: add SSRF-guarded CORS-scoped /apps/similar proxy ro c473b21 →