feat(sf): newbuilding_listings house-grain таблица для future-ЖК слоя (#975) #1108
1 changed files with 94 additions and 0 deletions
94
data/sql/135_newbuilding_listings.sql
Normal file
94
data/sql/135_newbuilding_listings.sql
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
-- 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;
|
||||
Loading…
Add table
Reference in a new issue