← back to Nationalrealestate

db/migrations/010_parcel_event.sql

39 lines

-- M-PH1: property historical-event log — the "all historical data" layer.
-- No BEGIN/COMMIT here — migrate.ts wraps each file in a transaction.
--
-- The `parcel` table holds only CURRENT state (one last_sale, current values).
-- parcel_event is the time-series: every recorded deed/sale, assessment year,
-- permit, and ownership change for a parcel, each carrying a source_url that
-- links straight back to the public record it came from. Keyed to a parcel by
-- (county_fips, source_id=APN) so it joins to `parcel` and the property page.

CREATE TABLE parcel_event (
  id           BIGSERIAL PRIMARY KEY,
  county_fips  TEXT NOT NULL,
  source_id    TEXT NOT NULL,          -- APN / BBL / PIN (matches parcel.source_id)
  event_type   TEXT NOT NULL,          -- deed | sale | assessment | permit | ownership
  event_date   DATE,
  amount       NUMERIC,                -- price / assessed value / permit valuation
  doc_type     TEXT,                   -- recorder doc type code
  doc_number   TEXT,                   -- recorder document number (links to ARCC)
  detail       JSONB,                  -- source-specific extra (buyer/seller, use, etc.)
  source       TEXT NOT NULL,          -- ingest source id, e.g. 'sandiego_ca'
  source_url   TEXT,                   -- PUBLIC-RECORD link back to the property/document
  created_at   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE (county_fips, source_id, event_type, event_date, doc_number)
);
CREATE INDEX idx_parcel_event_parcel ON parcel_event (county_fips, source_id, event_date DESC);
CREATE INDEX idx_parcel_event_type   ON parcel_event (event_type);

-- Per-parcel public-record deep links (assessor / recorder / GIS), so the
-- property page can always point the user at the authoritative source even
-- when the deep history itself is behind a paid/in-person wall (AB 1785).
CREATE TABLE parcel_links (
  county_fips TEXT NOT NULL,
  source_id   TEXT NOT NULL,
  kind        TEXT NOT NULL,           -- gis | assessor | recorder | tax | permits
  url         TEXT NOT NULL,
  label       TEXT,
  PRIMARY KEY (county_fips, source_id, kind)
);