gendesign/data/sql/124_domrf_kn_free_flats.sql
Light1YT 420cda2831
All checks were successful
Deploy / build-frontend (push) Has been skipped
Deploy / build-backend (push) Successful in 1m26s
Deploy / build-worker (push) Successful in 2m27s
Deploy / deploy (push) Successful in 1m7s
Deploy / changes (push) Successful in 5s
feat(domrf): persist domrf_kn_objects.free_flats (#950 PR A) (#1002)
2026-06-02 20:06:29 +00:00

66 lines
4.6 KiB
PL/PgSQL
Raw Permalink 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.

-- 124_domrf_kn_free_flats.sql
-- #950 (Site Finder v2 / 3-layer склад, EPIC 6) — PR A: persist free_flats.
--
-- Добавляет domrf_kn_objects.free_flats = freeFlatsInfo.numberFlats из kn-API dom.рф
-- (кол-во квартир ЖК В ТЕКУЩЕЙ ПРОДАЖЕ). Это единственный детерминированный сигнал,
-- разблокирующий object-level Layer2: hidden = flat_count free_flats
-- (flat_count = всего квартир по проектной декларации; free_flats = выставлено сейчас).
--
-- До #950 payload-блок freeFlatsInfo парсился ТОЛЬКО ради priceMin → price_min_rub
-- (миграция 113); numberFlats отбрасывался. Поле free_flats — sibling price_min_rub:
-- bigint→int, та же null-safety, тот же EXCLUDED-overwrite в UPSERT_OBJECT_SQL.
--
-- Backfill: сырой list объектов kn-API сохраняется как JSONB в domrf_raw_endpoints
-- (section='kn_api', endpoint='kn_object_place_<place>'; см. domrf_kn._insert_raw),
-- каждый элемент массива несёт objId + freeFlatsInfo.numberFlats. Поэтому historical
-- строки можно заполнить БЕЗ повторного скрейпа — join raw-элементов к
-- domrf_kn_objects по (obj_id, snapshot_date). Детерминированно, без LLM.
--
-- Deploy: auto-applied by deploy.yml через _schema_migrations tracking (ровно один раз).
-- Idempotent: ADD COLUMN IF NOT EXISTS + backfill только WHERE free_flats IS NULL.
-- Migration order: эта SQL-миграция применяется ПЕРВОЙ, backend domrf_kn.py — следом.
-- free_flats НЕnullable НЕ делаем: historical строки до бекфилла/без raw остаются NULL,
-- L2-сервис в этом случае падает на fallback (flat_count sold).
BEGIN;
-- ── 1. Колонка ─────────────────────────────────────────────────────────────────
ALTER TABLE domrf_kn_objects
ADD COLUMN IF NOT EXISTS free_flats integer;
COMMENT ON COLUMN domrf_kn_objects.free_flats IS
'Кол-во квартир в текущей продаже (freeFlatsInfo.numberFlats из kn-API dom.рф). '
'NULL = не заполнено (старый снапшот до #950). Для Layer2 hidden = flat_count free_flats.';
-- ── 2. Backfill из сохранённого raw-payload ────────────────────────────────────
-- domrf_raw_endpoints.payload — JSONB-массив сырых object-строк kn-API. Разворачиваем
-- его, тянем objId + freeFlatsInfo.numberFlats и матчим к уже записанным строкам по
-- (obj_id, snapshot_date). Только WHERE free_flats IS NULL — повторный прогон no-op.
-- Guard: payload должен быть массивом; objId и numberFlats — числовые строки (регексп
-- отсекает null/мусор ДО CAST, иначе invalid input syntax for type integer). Digit-cap
-- {1,9}/{1,18} держит numberFlats в int4, objId в bigint — отсекает overflow ДО CAST
-- (числа-кандидаты семантически ограничены квартирами одного ЖК, но guard явный).
-- DISTINCT ON (obj_id, snapshot_date): один objId не дублируется в одном снапшоте, но
-- на случай нескольких place-endpoint'ов на дату берём первый детерминированно.
WITH raw_flats AS (
SELECT DISTINCT ON (obj_id, r.snapshot_date)
CAST(elem->>'objId' AS bigint) AS obj_id,
r.snapshot_date AS snapshot_date,
CAST(elem->'freeFlatsInfo'->>'numberFlats' AS integer) AS free_flats
FROM domrf_raw_endpoints AS r
CROSS JOIN LATERAL jsonb_array_elements(r.payload) AS elem
WHERE r.section = 'kn_api'
AND r.endpoint LIKE 'kn_object_place_%'
AND jsonb_typeof(r.payload) = 'array'
AND elem->>'objId' ~ '^[0-9]{1,18}$'
AND elem->'freeFlatsInfo'->>'numberFlats' ~ '^[0-9]{1,9}$'
ORDER BY obj_id, r.snapshot_date
)
UPDATE domrf_kn_objects AS o
SET free_flats = rf.free_flats
FROM raw_flats AS rf
WHERE o.obj_id = rf.obj_id
AND o.snapshot_date = rf.snapshot_date
AND o.free_flats IS NULL;
COMMIT;