gendesign/data/sql/135_newbuilding_listings.sql
lekss361 f8aa1d7512
All checks were successful
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Successful in 1m23s
Deploy / build-frontend (push) Has been skipped
Deploy / build-worker (push) Successful in 2m32s
Deploy / deploy (push) Successful in 1m25s
feat(sf): newbuilding_listings house-grain таблица для future-ЖК слоя (#1108)
Миграция 135_newbuilding_listings.sql — house-grain таблица под листинги новостроек (зеркало ключевых колонок tradein.houses: гео/класс/застройщик/очередность/кадастр/cross-ref). geometry(Point,4326)+GIST, UNIQUE (source, ext_house_id), partial-индексы. Отдельно от objective_lots (lot-grain ≠ house-grain). Идемпотентна, 0 INSERT (наполнение ETL #976).

Closes #975.
Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
2026-06-06 21:48:51 +00:00

94 lines
6.4 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 135_newbuilding_listings.sql
-- #975 (SF-v2 #950 EPIC 6 «3-слойный склад», future-ЖК слой карты B4 / GG-форсайт).
--
-- На gendesign НЕТ таблицы под листинги новостроек (house-grain). Источник данных — tradein-БД
-- таблица `houses` (cian/yandex_newbuilding скраперы): building-grain, ~13k строк. `objective_lots`
-- на gendesign — LOT-grain (квартиры/лоты), зерно НЕ совпадает → переиспользовать нельзя.
--
-- РЕШЕНИЕ (objective_lots-reuse vs new, зафиксировано здесь по DoD #975): **новая house-grain
-- таблица**. objective_lots = лот (квартира), houses/newbuilding = дом/ЖК-корпус — разное зерно,
-- разные ключи, разные потребители (lots → market/velocity; newbuilding → future-supply карта).
-- Смешивать = grain-mismatch. Держим раздельно.
--
-- Зеркалит КЛЮЧЕВЫЕ колонки tradein.houses (не все 60 — только нужное SF future-ЖК слою + cross-ref):
-- идентификация/гео, класс/застройщик/рейтинг, очередность (houses_by_turn), кадастр, source-ids.
-- Наполняется cross-load ETL (#976 950-E5) идемпотентным upsert по uq (source, ext_house_id).
--
-- Зерно (grain): одна строка = один дом/корпус новостройки (house-grain). Дедуп-ключ —
-- (source, ext_house_id): ext_house_id — id дома в источнике (cian/yandex). re-load UPSERT-ит
-- (latest wins): crossloaded_at = freshness переноса.
--
-- Deploy: auto-applied deploy.yml через _schema_migrations (ровно один раз по NN). Idempotent:
-- CREATE TABLE/INDEX IF NOT EXISTS + guarded DO для UNIQUE (прецедент м.132/133/134).
-- НИКАКИХ строк здесь не вставляется (данные — из ETL #976).
BEGIN;
-- ── 1. Таблица newbuilding_listings ────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS newbuilding_listings (
id BIGSERIAL PRIMARY KEY,
source TEXT NOT NULL, -- 'cian' | 'yandex' | ... (источник дома)
ext_house_id TEXT NOT NULL, -- id дома в источнике (houses.ext_house_id) — дедуп
-- ── идентификация / гео ──
url TEXT, -- ссылка ЖК/дома (cian_zhk_url / listing url)
slug TEXT,
address TEXT,
full_address TEXT,
lat DOUBLE PRECISION,
lon DOUBLE PRECISION,
geom geometry(Point, 4326), -- точка дома (для future-ЖК слоя карты B4)
-- ── класс / застройщик / характеристики ──
house_class TEXT, -- класс ЖК (houses.house_class)
developer_name TEXT,
developer_key TEXT,
year_built INTEGER,
commission_year INTEGER, -- год ввода (план/факт)
total_floors INTEGER,
total_units INTEGER,
-- ── рейтинг / очередность / кадастр ──
rating NUMERIC,
reviews_count INTEGER,
houses_by_turn JSONB, -- очередность строительства (houses.houses_by_turn)
cadastral_number TEXT,
-- ── cross-ref на источники ──
cian_internal_house_id BIGINT,
yandex_jk_id TEXT,
raw_payload JSONB, -- полный houses.raw_payload (forward-compat)
source_last_scraped_at timestamptz, -- houses.last_scraped_at (свежесть источника)
crossloaded_at timestamptz NOT NULL DEFAULT now(), -- когда ETL #976 перенёс (freshness)
CONSTRAINT newbuilding_listings_source_chk CHECK (length(source) > 0),
CONSTRAINT newbuilding_listings_ext_id_chk CHECK (length(ext_house_id) > 0)
);
COMMENT ON TABLE newbuilding_listings IS
'Листинги новостроек (house-grain, #975 SF-v2 EPIC 6). Зеркало ключевых колонок tradein.houses '
'(cian/yandex_newbuilding) для future-ЖК слоя карты SF (B4). 1 строка = 1 дом/корпус. UPSERT '
'cross-load ETL (#976) по uq_newbuilding_listings (source, ext_house_id). НЕ objective_lots '
'(тот lot-grain) — отдельное зерно. analyze/карта читают будущее предложение через эту таблицу.';
COMMENT ON COLUMN newbuilding_listings.ext_house_id IS 'id дома в источнике (tradein.houses.ext_house_id); дедуп-ключ внутри source';
COMMENT ON COLUMN newbuilding_listings.houses_by_turn IS 'Очередность строительства (JSONB из houses.houses_by_turn) — для future-supply прогноза';
-- ── 2. Дедуп/UPSERT-ключ (source, ext_house_id) ────────────────────────────────
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'uq_newbuilding_listings'
) THEN
ALTER TABLE newbuilding_listings
ADD CONSTRAINT uq_newbuilding_listings
UNIQUE (source, ext_house_id);
END IF;
END
$$;
-- ── 3. Индексы ─────────────────────────────────────────────────────────────────
-- Главный read-path future-ЖК слоя: spatial (карта в bbox / overlap участка).
CREATE INDEX IF NOT EXISTS idx_newbuilding_listings_geom
ON newbuilding_listings USING GIST (geom);
-- Фильтры аналитики: по застройщику / классу.
CREATE INDEX IF NOT EXISTS idx_newbuilding_listings_developer
ON newbuilding_listings (developer_key) WHERE developer_key IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_newbuilding_listings_class
ON newbuilding_listings (house_class) WHERE house_class IS NOT NULL;
COMMIT;