← back to Commercialrealestate
scripts/db/broker-schema.sql
66 lines
-- broker-schema.sql — the broker/agent relationship graph for the CRE explorer (local Postgres "cre").
-- Nodes: firm, broker, listing. Edges: broker_listing (who lists what). Built for mind-mapping
-- connections — "which brokers co-list", "which firm dominates a submarket", "this agent's whole book".
CREATE TABLE IF NOT EXISTS firm (
id serial PRIMARY KEY,
name text UNIQUE NOT NULL,
created_at timestamptz DEFAULT now()
);
CREATE TABLE IF NOT EXISTS broker (
id serial PRIMARY KEY,
name text NOT NULL,
firm_id integer REFERENCES firm(id),
title text,
phone text,
email text,
crexi_id text, -- vendor broker id when available
source text, -- where we learned this broker (crexi, sheet, etc.)
created_at timestamptz DEFAULT now(),
UNIQUE (name, firm_id) -- a broker is unique within a firm
);
CREATE TABLE IF NOT EXISTS listing (
id text PRIMARY KEY, -- the CRE listing id (e.g. crx2560155)
address text,
city text,
zip text,
type text,
price bigint,
cap_rate numeric,
units integer,
firm_name text, -- listing brokerage (denormalized for convenience)
source text,
created_at timestamptz DEFAULT now()
);
-- The edge table: a broker is on a listing (lead/co/team). This is the graph that powers the mind-map.
CREATE TABLE IF NOT EXISTS broker_listing (
broker_id integer NOT NULL REFERENCES broker(id) ON DELETE CASCADE,
listing_id text NOT NULL REFERENCES listing(id) ON DELETE CASCADE,
role text DEFAULT 'broker', -- lead | co | team
PRIMARY KEY (broker_id, listing_id)
);
CREATE INDEX IF NOT EXISTS idx_broker_firm ON broker(firm_id);
CREATE INDEX IF NOT EXISTS idx_listing_city ON listing(city);
CREATE INDEX IF NOT EXISTS idx_listing_firm ON listing(firm_name);
CREATE INDEX IF NOT EXISTS idx_bl_listing ON broker_listing(listing_id);
-- A convenience view: broker with firm name + listing count (node weight for the mind-map).
CREATE OR REPLACE VIEW broker_node AS
SELECT b.id, b.name, f.name AS firm, b.phone, b.email, b.title,
count(bl.listing_id) AS listings
FROM broker b
LEFT JOIN firm f ON f.id = b.firm_id
LEFT JOIN broker_listing bl ON bl.broker_id = b.id
GROUP BY b.id, f.name;
-- Co-listing edges: pairs of brokers who appear on the same listing (the connection graph).
CREATE OR REPLACE VIEW broker_cobroker AS
SELECT a.broker_id AS a, b.broker_id AS b, count(*) AS shared_listings
FROM broker_listing a
JOIN broker_listing b ON a.listing_id = b.listing_id AND a.broker_id < b.broker_id
GROUP BY a.broker_id, b.broker_id;