← 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 →