[object Object]

← back to Rentv

server: Cody gate — proper DRILL-DOWN faceting in /api/unified-search (two independent passes). My first fix counted facets ignoring BOTH build and type; the correct model is per-facet exclusion: by_build honors an active type filter (excludes its own build), by_type honors an active build filter (excludes its own) — so each chip's count matches what selecting it yields even with the other facet active. Verified live: build=rentv → by_build.rentv === total

e9b3e49ff7813e914caaa3dfde76220652426305 · 2026-08-06 02:56:58 -0700 · Steve

Files touched

Diff

commit e9b3e49ff7813e914caaa3dfde76220652426305
Author: Steve <steve@designerwallcoverings.com>
Date:   Thu Aug 6 02:56:58 2026 -0700

    server: Cody gate — proper DRILL-DOWN faceting in /api/unified-search (two independent passes). My first fix counted facets ignoring BOTH build and type; the correct model is per-facet exclusion: by_build honors an active type filter (excludes its own build), by_type honors an active build filter (excludes its own) — so each chip's count matches what selecting it yields even with the other facet active. Verified live: build=rentv → by_build.rentv === total
---
 server.js | 23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)

diff --git a/server.js b/server.js
index a9466432..380da1e5 100644
--- a/server.js
+++ b/server.js
@@ -353,17 +353,20 @@ app.get('/api/unified-search', adminOnly, (req, r) => {
   const min = +req.query.min || 0, max = +req.query.max || 0;
   const ql = String(req.query.q || '').trim().toLowerCase();
   const terms = ql ? ql.split(/\s+/).filter(Boolean) : [];
-  let out = rows;
-  // Filters that must NOT reset the facet chips run first; the build/type chips are counted over THIS
-  // set (below) so they always show a count for every build/type — even when one is already selected.
-  if (year) out = out.filter((x) => String(x.date || '').startsWith(String(year)));
-  if (min) out = out.filter((x) => (x.amount || 0) >= min);
-  if (max) out = out.filter((x) => (x.amount || 0) <= max);
-  if (terms.length) out = out.filter((x) => { const t = x._t || ''; return terms.every((w) => t.includes(w)); });
-  // facet counts (over the term/amount-filtered set, before build/type narrowing) for the UI chips
+  // year/amount/term filters apply to everything (facets + results); build/type are the drill-down facets.
+  let base = rows;
+  if (year) base = base.filter((x) => String(x.date || '').startsWith(String(year)));
+  if (min) base = base.filter((x) => (x.amount || 0) >= min);
+  if (max) base = base.filter((x) => (x.amount || 0) <= max);
+  if (terms.length) base = base.filter((x) => { const t = x._t || ''; return terms.every((w) => t.includes(w)); });
+  // Drill-down facet counts: by_build honors an active TYPE filter (but excludes its own build filter),
+  // by_type honors an active BUILD filter (but excludes its own) — so each chip's number matches what
+  // selecting it will actually yield, and the user can still see + switch to other builds/types.
   const byBuild = {}, byType = {};
-  for (const x of out) { byBuild[x.build] = (byBuild[x.build] || 0) + 1; byType[x.type] = (byType[x.type] || 0) + 1; }
-  // NOW narrow to the selected build/type for the actual result rows (facets already counted above)
+  for (const x of (type ? base.filter((r) => r.type === type) : base)) byBuild[x.build] = (byBuild[x.build] || 0) + 1;
+  for (const x of (build ? base.filter((r) => r.build === build) : base)) byType[x.type] = (byType[x.type] || 0) + 1;
+  // actual result rows: narrowed by BOTH selected facets
+  let out = base;
   if (build) out = out.filter((x) => x.build === build);
   if (type) out = out.filter((x) => x.type === type);
   // rank: title hit > recency > amount

← e8952c25 server: fix broken facet counts in /api/unified-search (the  ·  back to Rentv  ·  auto-save: 2026-08-06T03:18:00 (7 files) — data/deals-regist 556ba576 →