← back to Commercialrealestate
CRCP: broker-direct SUBSTITUTES the aggregator row (sub out Crexi when the firm's own listing is found) — Steve 2026-08-18
6074d06ac2cfa7d7af3a9476c31ae7145ab2e1cd · 2026-08-18 20:50:48 -0700 · Steve Abrams
- mergedRanked(): canonical street-address key (Blvd=Boulevard etc) so a broker-direct listing matches its Crexi twin; on match SWAP source -> firm's own page + re-attribute + fill gaps instead of skipping. 41 subbed on first pass.
- _brokerDirectSubbed counter; total tightened (no same-property double rows)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 6074d06ac2cfa7d7af3a9476c31ae7145ab2e1cd
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue Aug 18 20:50:48 2026 -0700
CRCP: broker-direct SUBSTITUTES the aggregator row (sub out Crexi when the firm's own listing is found) — Steve 2026-08-18
- mergedRanked(): canonical street-address key (Blvd=Boulevard etc) so a broker-direct listing matches its Crexi twin; on match SWAP source -> firm's own page + re-attribute + fill gaps instead of skipping. 41 subbed on first pass.
- _brokerDirectSubbed counter; total tightened (no same-property double rows)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
scripts/serve.js | 48 +++++++++++++++++++++++++++++++++++++-----------
1 file changed, 37 insertions(+), 11 deletions(-)
diff --git a/scripts/serve.js b/scripts/serve.js
index e49aae0..39da358 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -1990,20 +1990,46 @@ function mergedRanked() {
if (_rankedMerge.key === key && _rankedMerge.json) return _rankedMerge.json;
const base = JSON.parse(fs.readFileSync(rankedPath, 'utf8'));
const ranked = Array.isArray(base.ranked) ? base.ranked : [];
- const addrKey = r => String(r.address || '').toLowerCase().trim() + '|' + String(r.zip || '').trim();
- const have = new Set(ranked.map(addrKey));
- let added = 0;
+ // Canonical street-address key so a broker-direct "1351 Westwood Blvd" matches a Crexi
+ // "1351 Westwood Boulevard" (suffix words normalized, then non-alphanumerics stripped).
+ const SUF = { boulevard: 'blvd', avenue: 'ave', street: 'st', drive: 'dr', road: 'rd', place: 'pl',
+ court: 'ct', lane: 'ln', terrace: 'ter', parkway: 'pkwy', highway: 'hwy', square: 'sq', circle: 'cir',
+ north: 'n', south: 's', east: 'e', west: 'w' };
+ const addrKey = r => String(r.address || '').toLowerCase()
+ .replace(/\b(boulevard|avenue|street|drive|road|place|court|lane|terrace|parkway|highway|square|circle|north|south|east|west)\b/g, m => SUF[m])
+ .replace(/[^a-z0-9]/g, '');
+ const idxByAddr = new Map();
+ ranked.forEach((r, i) => { const k = addrKey(r); if (k && !idxByAddr.has(k)) idxByAddr.set(k, i); });
+
+ // Steve 2026-08-18: "sub out crexi if found in data" — a broker-DIRECT listing (the firm's own site,
+ // real permalink + attribution, higher trust) SUBSTITUTES the aggregator row for the same property
+ // instead of being skipped. We keep the existing row's richer computed/structured fields but swap its
+ // source → the broker's own listing page, re-attribute the firm, and fill any gaps from the direct row.
+ let added = 0, substituted = 0;
for (const f of BROKER_FEEDS) {
- try {
- const feed = JSON.parse(fs.readFileSync(path.join(ROOT, 'data', f), 'utf8'));
- for (const row of (feed.listings || [])) {
- const k = addrKey(row);
- if (have.has(k)) continue; // a crexi row already covers this address
- have.add(k); ranked.push(row); added++;
+ let feed; try { feed = JSON.parse(fs.readFileSync(path.join(ROOT, 'data', f), 'utf8')); } catch (_) { continue; }
+ for (const row of (feed.listings || [])) {
+ const k = addrKey(row); if (!k) continue;
+ if (idxByAddr.has(k)) {
+ const i = idxByAddr.get(k); const ex = ranked[i];
+ if (ex._sourced) continue; // already a broker-direct row → don't double-sub
+ ranked[i] = { ...ex,
+ source: row.source, source_host: row.source_host, source_of_truth: 'broker-direct',
+ broker_firm: row.broker_firm || ex.broker_firm, firm: row.firm || ex.firm,
+ _sourced: row._sourced, _subbedFrom: ex.source_host || 'aggregator',
+ // fill gaps from the direct listing where the aggregator row was null
+ price: ex.price != null ? ex.price : row.price,
+ cap_rate: ex.cap_rate != null ? ex.cap_rate : row.cap_rate,
+ units: ex.units != null ? ex.units : row.units,
+ type: ex.type || row.type,
+ };
+ substituted++;
+ } else {
+ idxByAddr.set(k, ranked.length); ranked.push(row); added++;
}
- } catch (_) { /* a missing/!valid feed just contributes nothing */ }
+ }
}
- const out = { ...base, ranked, _brokerDirectAdded: added };
+ const out = { ...base, ranked, _brokerDirectAdded: added, _brokerDirectSubbed: substituted };
_rankedMerge = { key, json: out };
return out;
}
← 4eac4b6 CRCP gov-agents.html: add grid/list/table view system (was t
·
back to Commercialrealestate
·
crcp-theme: theme fleet custom SURFACE vars (--panel/--panel d5a0b83 →