← back to Sublease Agentabrams
scripts/schema.sql
77 lines
-- CRUnifiedDB — unified commercial-real-estate database for sublease.agentabrams.com
-- Mirrors the dw_unified pattern: per-firm crawler agents write here; the app reads here.
CREATE TABLE IF NOT EXISTS sponsors (
id SERIAL PRIMARY KEY,
slug TEXT UNIQUE NOT NULL, -- loopnet, cbre, naicapital ...
name TEXT NOT NULL,
kind TEXT NOT NULL, -- marketplace | brokerage | operator | lender | data
role TEXT NOT NULL DEFAULT 'broker',-- broker | financing_partner
tagline TEXT,
website TEXT,
listings_url TEXT, -- where the crawler starts
logo_path TEXT, -- /assets/logos/<slug>.jpg
crawl_enabled BOOLEAN NOT NULL DEFAULT TRUE,
tos_risk TEXT NOT NULL DEFAULT 'low', -- low | medium | high (CoStar/LoopNet = high)
notes TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS listings (
id SERIAL PRIMARY KEY,
sponsor_id INTEGER NOT NULL REFERENCES sponsors(id) ON DELETE CASCADE,
external_id TEXT, -- the firm's own listing id
title TEXT,
address TEXT,
city TEXT,
state TEXT DEFAULT 'CA',
zip TEXT,
lat DOUBLE PRECISION,
lng DOUBLE PRECISION,
space_type TEXT, -- office | retail | industrial | flex | medical | land
is_sublease BOOLEAN NOT NULL DEFAULT TRUE,
size_sf INTEGER, -- rentable square feet
price_amount NUMERIC, -- rate value
price_unit TEXT, -- $/SF/yr, $/SF/mo, $/mo, negotiable
term TEXT, -- remaining sublease term
available_on TEXT,
source_url TEXT,
image_url TEXT,
description TEXT,
status TEXT NOT NULL DEFAULT 'active',-- active | pending | off_market
raw JSONB,
first_seen TIMESTAMPTZ NOT NULL DEFAULT now(),
last_seen TIMESTAMPTZ NOT NULL DEFAULT now(),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (sponsor_id, external_id)
);
CREATE INDEX IF NOT EXISTS idx_listings_sponsor ON listings(sponsor_id);
CREATE INDEX IF NOT EXISTS idx_listings_geo ON listings(lat, lng);
CREATE INDEX IF NOT EXISTS idx_listings_type ON listings(space_type);
-- Individual agents/reps within a firm (populated by crawlers where available)
CREATE TABLE IF NOT EXISTS agents (
id SERIAL PRIMARY KEY,
sponsor_id INTEGER NOT NULL REFERENCES sponsors(id) ON DELETE CASCADE,
name TEXT NOT NULL,
title TEXT,
email TEXT,
phone TEXT,
profile_url TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (sponsor_id, name)
);
-- One row per crawler execution — the audit/cost trail
CREATE TABLE IF NOT EXISTS crawl_runs (
id SERIAL PRIMARY KEY,
sponsor_id INTEGER REFERENCES sponsors(id) ON DELETE CASCADE,
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
finished_at TIMESTAMPTZ,
status TEXT NOT NULL DEFAULT 'running', -- running | ok | error | blocked
listings_found INTEGER DEFAULT 0,
listings_new INTEGER DEFAULT 0,
cost_usd NUMERIC DEFAULT 0,
notes TEXT
);