feat(tradein): SQL migration 029 — extend matching/valuation/dynamics tables (PR #445 follow-up) (#448)
This commit is contained in:
parent
9ba776f2ca
commit
7fad898c7f
1 changed files with 184 additions and 0 deletions
|
|
@ -0,0 +1,184 @@
|
|||
-- 029_extend_matching_valuation_dynamics.sql
|
||||
-- Purpose: Extend matching/valuation/dynamics tables per PR #445 deep-code-reviewer follow-ups.
|
||||
-- Addresses 3 MEDIUM blockers:
|
||||
-- A. house_sources — add ext_url / raw_payload / last_seen_at (Stage 8 conflict resolution)
|
||||
-- B. listing_sources — add price_rub / area_m2 / floor / rooms_count / raw_payload / last_seen_at
|
||||
-- C. external_valuations — split sale/rent + chart_change_pct/dir + filters_hash + external_house_id
|
||||
-- (Stage 7 Cian Valuation full save)
|
||||
-- D. houses_price_dynamics — add room_count / prices_type / period dimensions + replace UNIQUE
|
||||
-- (Stage 6 newbuilding multi-dim chart)
|
||||
-- E. houses — add cadastral_number for Tier-0 Cian Valuation house matching
|
||||
--
|
||||
-- Dependencies (must already exist):
|
||||
-- - house_sources (028_matching_tables.sql)
|
||||
-- - listing_sources (028_matching_tables.sql)
|
||||
-- - external_valuations (026_external_valuations.sql)
|
||||
-- - houses_price_dynamics (024_houses_price_dynamics.sql)
|
||||
-- - houses (009_houses.sql)
|
||||
--
|
||||
-- Deploy order: Apply after 028.
|
||||
-- Re-run safe: all ADD COLUMN IF NOT EXISTS; DO $$ blocks guard constraint changes.
|
||||
--
|
||||
-- NOTE on column naming: house_sources uses (ext_source, ext_id) per 028 DDL.
|
||||
-- listing_sources uses (ext_source, ext_id, source_url, last_scraped_at) per 028 DDL.
|
||||
-- We add complementary columns; existing column aliases documented inline.
|
||||
--
|
||||
-- Source refs:
|
||||
-- Cross_Source_Matching_Strategy sec 2.1 (house_sources columns)
|
||||
-- Cross_Source_Matching_Strategy sec 2.2 (listing_sources columns)
|
||||
-- Schema_Cian_SERP_Inventory sec 26.3 (external_valuations full shape)
|
||||
-- Schema_Cian_SERP_Inventory sec 16.3 (houses_price_dynamics multi-dim structure)
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- =============================================================================
|
||||
-- A. Extend house_sources
|
||||
-- Add: ext_url, raw_payload, last_seen_at
|
||||
-- (ext_source/ext_id/confidence/matched_method/matched_at already in 028)
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE house_sources
|
||||
ADD COLUMN IF NOT EXISTS ext_url text, -- canonical URL to the source listing/page
|
||||
ADD COLUMN IF NOT EXISTS raw_payload jsonb, -- full source-side payload snapshot for diffing
|
||||
ADD COLUMN IF NOT EXISTS last_seen_at timestamptz; -- last time this house was observed from this source
|
||||
|
||||
-- GIN index for raw_payload JSON querying (diff, field extraction)
|
||||
CREATE INDEX IF NOT EXISTS house_sources_raw_payload_gin_idx
|
||||
ON house_sources USING GIN (raw_payload)
|
||||
WHERE raw_payload IS NOT NULL;
|
||||
|
||||
-- Recency index for stale-data detection per source
|
||||
CREATE INDEX IF NOT EXISTS house_sources_last_seen_idx
|
||||
ON house_sources (last_seen_at DESC)
|
||||
WHERE last_seen_at IS NOT NULL;
|
||||
|
||||
-- =============================================================================
|
||||
-- B. Extend listing_sources
|
||||
-- Add: price_rub, area_m2, floor, rooms_count, raw_payload, last_seen_at
|
||||
-- (ext_source/ext_id/confidence/matched_method/source_url/last_scraped_at already in 028)
|
||||
-- Note: last_seen_at is a semantic alias for last_scraped_at — both kept for compat.
|
||||
-- New code should write to last_seen_at; last_scraped_at preserved for existing queries.
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE listing_sources
|
||||
ADD COLUMN IF NOT EXISTS price_rub bigint, -- price from this source at last scrape (cross-source divergence)
|
||||
ADD COLUMN IF NOT EXISTS area_m2 numeric(8,2), -- area as seen on this source
|
||||
ADD COLUMN IF NOT EXISTS floor int, -- floor from this source
|
||||
ADD COLUMN IF NOT EXISTS rooms_count int, -- rooms count from this source
|
||||
ADD COLUMN IF NOT EXISTS raw_payload jsonb, -- full source-side payload snapshot
|
||||
ADD COLUMN IF NOT EXISTS last_seen_at timestamptz; -- last time we saw this listing on source
|
||||
|
||||
-- GIN index for raw_payload JSON querying
|
||||
CREATE INDEX IF NOT EXISTS listing_sources_raw_payload_gin_idx
|
||||
ON listing_sources USING GIN (raw_payload)
|
||||
WHERE raw_payload IS NOT NULL;
|
||||
|
||||
-- Recency index (complement existing listing_sources_scraped_idx on last_scraped_at)
|
||||
CREATE INDEX IF NOT EXISTS listing_sources_last_seen_idx
|
||||
ON listing_sources (last_seen_at DESC)
|
||||
WHERE last_seen_at IS NOT NULL;
|
||||
|
||||
-- =============================================================================
|
||||
-- C. Extend external_valuations
|
||||
-- Add: sale_price_rub, sale_accuracy, rent_price_rub, rent_accuracy,
|
||||
-- chart_change_pct, chart_change_direction, filters_hash, external_house_id
|
||||
-- Existing: price_rub (primary estimate), accuracy (generic), chart, raw_payload
|
||||
-- New columns provide sale/rent split and Cian-specific metadata.
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE external_valuations
|
||||
-- Sale estimate (split from generic price_rub / accuracy)
|
||||
ADD COLUMN IF NOT EXISTS sale_price_rub numeric, -- sale price estimate
|
||||
ADD COLUMN IF NOT EXISTS sale_accuracy numeric, -- sale accuracy 0-100 (Cian-specific)
|
||||
-- Rent estimate (Cian only)
|
||||
ADD COLUMN IF NOT EXISTS rent_price_rub numeric, -- rent price per month estimate
|
||||
ADD COLUMN IF NOT EXISTS rent_accuracy numeric, -- rent accuracy 0-100
|
||||
-- Chart-level summary (from estimationChart)
|
||||
ADD COLUMN IF NOT EXISTS chart_change_pct numeric(5,2), -- price change % since chart start
|
||||
ADD COLUMN IF NOT EXISTS chart_change_direction text, -- 'increase' / 'decrease' / 'neutral'
|
||||
-- Cian internal metadata
|
||||
ADD COLUMN IF NOT EXISTS filters_hash text, -- sha256 of normalized filter params (dedup slight variations)
|
||||
ADD COLUMN IF NOT EXISTS external_house_id bigint; -- Cian's internal houseId (filters.houseId)
|
||||
|
||||
-- Index on filters_hash for dedup queries
|
||||
CREATE INDEX IF NOT EXISTS ev_filters_hash_idx
|
||||
ON external_valuations (filters_hash)
|
||||
WHERE filters_hash IS NOT NULL;
|
||||
|
||||
-- Index on external_house_id for Cian house matching lookups
|
||||
CREATE INDEX IF NOT EXISTS ev_external_house_id_idx
|
||||
ON external_valuations (external_house_id)
|
||||
WHERE external_house_id IS NOT NULL;
|
||||
|
||||
-- =============================================================================
|
||||
-- D. Extend houses_price_dynamics
|
||||
-- Add: room_count text, prices_type text, period text (dimension columns)
|
||||
-- Replace UNIQUE(house_id, month_date, source) with multi-dim UNIQUE.
|
||||
--
|
||||
-- room_count uses Cian API enum text values:
|
||||
-- 'studio'/'oneRoom'/'twoRoom'/'threeRoom'/'fourPlusRooms' / 'all' (aggregate)
|
||||
-- prices_type: 'price' (total) / 'priceSqm' (per sqm)
|
||||
-- period: 'halfYear' / 'year' / 'allTime' — chart time window
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE houses_price_dynamics
|
||||
ADD COLUMN IF NOT EXISTS room_count text, -- Cian room enum or 'all' for aggregate
|
||||
ADD COLUMN IF NOT EXISTS prices_type text, -- 'price' / 'priceSqm'
|
||||
ADD COLUMN IF NOT EXISTS period text; -- 'halfYear' / 'year' / 'allTime'
|
||||
|
||||
-- Backfill existing rows so they have non-NULL dimension values.
|
||||
-- Historical rows had no dimension granularity; mark as aggregate/default.
|
||||
UPDATE houses_price_dynamics
|
||||
SET
|
||||
room_count = COALESCE(room_count, 'all'),
|
||||
prices_type = COALESCE(prices_type, 'priceSqm'),
|
||||
period = COALESCE(period, 'allTime')
|
||||
WHERE room_count IS NULL OR prices_type IS NULL OR period IS NULL;
|
||||
|
||||
-- Replace old 3-column UNIQUE with 6-column multi-dimension version.
|
||||
-- Idempotent: checks pg_constraint by name before each action.
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Drop old constraint if it still exists (default PG naming convention)
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conname = 'houses_price_dynamics_house_id_month_date_source_key'
|
||||
AND conrelid = 'houses_price_dynamics'::regclass
|
||||
) THEN
|
||||
ALTER TABLE houses_price_dynamics
|
||||
DROP CONSTRAINT houses_price_dynamics_house_id_month_date_source_key;
|
||||
END IF;
|
||||
|
||||
-- Add new constraint only if not already present (safe re-run)
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint
|
||||
WHERE conname = 'houses_price_dynamics_dim_key'
|
||||
AND conrelid = 'houses_price_dynamics'::regclass
|
||||
) THEN
|
||||
ALTER TABLE houses_price_dynamics
|
||||
ADD CONSTRAINT houses_price_dynamics_dim_key
|
||||
UNIQUE (house_id, source, room_count, prices_type, period, month_date);
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
-- Supporting index for multi-dim read pattern (complements existing hpd_house_date_idx)
|
||||
CREATE INDEX IF NOT EXISTS hpd_house_dim_idx
|
||||
ON houses_price_dynamics (house_id, source, room_count, prices_type, period, month_date DESC);
|
||||
|
||||
-- =============================================================================
|
||||
-- E. Extend houses — add cadastral_number for Tier-0 house matching
|
||||
-- Enables: SELECT id FROM houses WHERE cadastral_number = :ck (confidence=1.0)
|
||||
-- Sources: Cian offer.buildingCadastralNumber, cad_buildings.kadastr_num, DomRF KN
|
||||
-- Format: '66:41:0000000:12345' (standard cadastral format)
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE houses
|
||||
ADD COLUMN IF NOT EXISTS cadastral_number text; -- building cadastral number
|
||||
|
||||
-- BTree index for exact Tier-0 match lookups
|
||||
CREATE INDEX IF NOT EXISTS houses_cadastral_number_idx
|
||||
ON houses (cadastral_number)
|
||||
WHERE cadastral_number IS NOT NULL;
|
||||
|
||||
COMMIT;
|
||||
Loading…
Add table
Reference in a new issue