← back to Dw Photo Capture
All-Shopify search covers collections too: parallel product + collection-products query, merged/deduped, across every vendor; results carry their collections
3cbfbc0bd328bc1cd0f6cb39e694c0485996da10 · 2026-06-24 19:16:31 -0700 · steve@designerwallcoverings.com
Files touched
M data/build.jsonM public/index.htmlM server.js
Diff
commit 3cbfbc0bd328bc1cd0f6cb39e694c0485996da10
Author: steve@designerwallcoverings.com <steve@designerwallcoverings.com>
Date: Wed Jun 24 19:16:31 2026 -0700
All-Shopify search covers collections too: parallel product + collection-products query, merged/deduped, across every vendor; results carry their collections
---
data/build.json | 5 +++--
public/index.html | 2 +-
server.js | 34 +++++++++++++++++++++++-----------
3 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/data/build.json b/data/build.json
index e19e8a3..03da503 100644
--- a/data/build.json
+++ b/data/build.json
@@ -1,5 +1,5 @@
{
- "next": 18,
+ "next": 19,
"map": {
"578af86f": 2,
"bc95fdb0": 3,
@@ -16,6 +16,7 @@
"3a1be30f": 14,
"d0f0df55": 15,
"9905b54e": 16,
- "48c9e659": 17
+ "48c9e659": 17,
+ "779f6d1f": 18
}
}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index dfb8f18..b202f65 100644
--- a/public/index.html
+++ b/public/index.html
@@ -600,7 +600,7 @@ $('#sort').addEventListener('change',e=>{setLS('sort',e.target.value);render();}
document.querySelectorAll('.chip').forEach(c=>c.addEventListener('click',()=>{
document.querySelectorAll('.chip').forEach(z=>z.classList.remove('on'));c.classList.add('on');filter=c.dataset.f;setLS('filter',filter);
if(filter==='any'){ $('#q').placeholder='🔎 SKU, model #, name, or collection…'; $('#q').focus(); doLookup(); }
- else if(filter==='shop'){ $('#q').placeholder='🌐 Any Shopify SKU, name, or vendor…'; $('#q').focus(); doShopSearch(); }
+ else if(filter==='shop'){ $('#q').placeholder='🌐 Any Shopify SKU, name, vendor, or collection…'; $('#q').focus(); doShopSearch(); }
else if(filter==='new'){ $('#q').placeholder='🔎 Filter the new SKUs…'; loadNew(); }
else { $('#q').placeholder='🔎 Search name or model #…'; render(); }
}));
diff --git a/server.js b/server.js
index 8c72562..2fe7589 100644
--- a/server.js
+++ b/server.js
@@ -444,21 +444,33 @@ function nodeToItem(n, extra) {
// Live store-wide search: one GraphQL call, wildcard match on sku/title/vendor.
// keep_images:true is stamped on every result so a photo update on an arbitrary
// live product ADDS as featured without wiping existing imagery.
+const _PNODE = `legacyResourceId title status vendor
+ imgs:images(first:1){edges{node{src}}}
+ cols:collections(first:6){edges{node{title}}}
+ mf:metafield(namespace:"custom",key:"manufacturer_sku"){value}
+ mf2:metafield(namespace:"dwc",key:"manufacturer_sku"){value}
+ v:variants(first:5){edges{node{sku title price}}}`;
async function shopifySearch(q) {
const terms = q.split(/\s+/).filter(Boolean).slice(0, 4)
.map(t => t.replace(/["\\():*]/g, '')).filter(Boolean);
if (!terms.length) return [];
- const qstr = terms.map(t => `(sku:*${t}* OR title:*${t}* OR vendor:*${t}*)`).join(' AND ');
- const Q = `query($q:String){ products(first:60, query:$q){
- edges{node{ legacyResourceId title status vendor
- imgs:images(first:1){edges{node{src}}}
- mf:metafield(namespace:"custom",key:"manufacturer_sku"){value}
- mf2:metafield(namespace:"dwc",key:"manufacturer_sku"){value}
- v:variants(first:5){edges{node{sku title price}}} } } } }`;
- const r = await gql(Q, { q: qstr });
- const edges = (r.data && r.data.products && r.data.products.edges) || [];
- return edges.map(e => e.node).filter(n => n.status !== 'ARCHIVED')
- .map(n => nodeToItem(n, { keep_images: true, scope: 'shopify' }));
+ // 1) product search (sku/title/vendor) + 2) products inside any matching COLLECTION — in parallel
+ const pstr = terms.map(t => `(sku:*${t}* OR title:*${t}* OR vendor:*${t}*)`).join(' AND ');
+ const cstr = terms.map(t => `title:*${t}*`).join(' AND ');
+ const PQ = `query($q:String){ products(first:50, query:$q){ edges{node{ ${_PNODE} } } } }`;
+ const CQ = `query($q:String){ collections(first:5, query:$q){ edges{node{ title products(first:40){edges{node{ ${_PNODE} }}} }}} }`;
+ const [pr, cr] = await Promise.all([gql(PQ, { q: pstr }), gql(CQ, { q: cstr })]);
+ const byId = new Map();
+ const add = (n, fromCol) => {
+ if (!n || n.status === 'ARCHIVED') return;
+ let it = byId.get(n.legacyResourceId);
+ if (!it) { it = nodeToItem(n, { keep_images: true, scope: 'shopify' }); it.collections = (n.cols ? n.cols.edges.map(e => e.node.title) : []); byId.set(n.legacyResourceId, it); }
+ if (fromCol && !it.collections.includes(fromCol)) it.collections.push(fromCol);
+ };
+ for (const e of ((pr.data && pr.data.products && pr.data.products.edges) || [])) add(e.node);
+ for (const ce of ((cr.data && cr.data.collections && cr.data.collections.edges) || []))
+ for (const pe of ce.node.products.edges) add(pe.node, ce.node.title);
+ return Array.from(byId.values()).slice(0, 80);
}
server.listen(PORT, '0.0.0.0', () => {
← 418e972 Add collections to search: each product carries its Shopify
·
back to Dw Photo Capture
·
Add ⭐ Favorites + 🕘 Recent views: star any card to pin SKUs 3c7d0a2 →