← back to Nationalrealestate
scripts/classify-asset-class.sql
61 lines
-- classify-asset-class.sql — idempotent commercial/residential classifier for usre.
-- Re-runnable: safe to run any number of times. NEVER overwrites a row whose
-- asset_class_source = 'manual' (human override wins).
--
-- Rule (evaluated top-down, first match wins):
-- 1. BRAND match -> commercial (a named national CRE house; wins even if the name also
-- contains a lender word, e.g. "Berkadia Commercial Mortgage")
-- 2. "residential" in name -> residential (explicit; keeps Coldwell Banker RESIDENTIAL out)
-- 3. NON-BROKER match -> residential (mortgage / lender / insurer / escrow / title / bank /
-- appraiser / property-mgmt: NOT a CRE brokerage, so it must
-- not appear on RENTV's CRE broker desk. Only reached when
-- no CRE brand matched, so it can't demote a real CRE house.)
-- 4. KEYWORD match -> commercial (commercial real estate / net lease / capital markets /
-- investment sales / industrial realty / bare "commercial" / cre)
-- 5. else -> residential (the ~215k-firm registry is overwhelmingly residential)
-- Brokers inherit their firm's class; firm-less brokers default residential.
-- 1) Named national CRE houses. Short/ambiguous tokens use word boundaries (\y).
\set brand_rx '\\y(cbre|jll|nai|cbc|svn|ipa)\\y|jones lang lasalle|cushman|colliers|newmark|marcus (&|and) millichap|kidder mathews|lee (&|and) associates|avison young|savills|cresa|institutional property advisors|stream realty|matthews (real estate|reis)|northmarq|berkadia|walker (&|and) dunlop|eastdil|transwestern|srs real estate|hanley investment|coldwell banker commercial|keller williams commercial|sperry van ness|tcn worldwide|voit real estate|daum commercial'
-- 4) Generic commercial signals (weaker than a brand; gated by the non-broker guard above).
\set kw_rx 'commercial real estate|\\ycommercial\\y|net lease|capital markets|investment sales|industrial realty|\\ycre\\y'
-- 3) Not a CRE brokerage — lenders, insurers, title/escrow, banks, appraisers, property managers.
\set nonbroker_rx 'mortgage|lending|\\yloans?\\y|insurance|escrow|\\ytitle\\y|\\ybank\\y|apprais|property management'
BEGIN;
UPDATE firm
SET asset_class = CASE
WHEN lower(coalesce(normalized_name, name)) ~ :'brand_rx' THEN 'commercial'
WHEN lower(coalesce(normalized_name, name)) ~ '\yresidential\y' THEN 'residential'
WHEN lower(coalesce(normalized_name, name)) ~ :'nonbroker_rx' THEN 'residential'
WHEN lower(coalesce(normalized_name, name)) ~ :'kw_rx' THEN 'commercial'
ELSE 'residential'
END,
asset_class_source = 'seed'
WHERE asset_class_source IS DISTINCT FROM 'manual';
-- Brokers with a firm inherit the firm's class.
UPDATE broker b
SET asset_class = f.asset_class,
asset_class_source = 'inherited'
FROM firm f
WHERE b.firm_id = f.id
AND b.asset_class_source IS DISTINCT FROM 'manual';
-- Firm-less brokers (standalone DRE licensees): residential by default. RENTV drops these via
-- firm_id IS NOT NULL anyway, but tagging keeps usreal/CRCP correct.
UPDATE broker
SET asset_class = 'residential',
asset_class_source = 'seed'
WHERE firm_id IS NULL
AND asset_class_source IS DISTINCT FROM 'manual';
COMMIT;
-- report
SELECT 'firm' AS tbl, asset_class, count(*) FROM firm GROUP BY asset_class
UNION ALL
SELECT 'broker' AS tbl, asset_class, count(*) FROM broker GROUP BY asset_class
ORDER BY tbl, asset_class;