fix(scrapers): COALESCE-backfill listing_date/area_m2 in save_listings ON CONFLICT (Refs #821) #829

Merged
bot-reviewer merged 1 commit from feat/821-coalesce-backfill into main 2026-05-30 19:58:07 +00:00
Collaborator

Что

save_listings (base.py) на INSERT ... ON CONFLICT (dedup_hash) DO UPDATE НЕ перечислял listing_date и area_m2 в SET-блоке → на re-scrape (UPDATE-ветка, ~99% строк) эти поля никогда не backfill'ились. Прод: avito listing_date coverage 2.3% и не рос.

Как (точный контракт #821)

В SET-блок (после metro_stations = EXCLUDED.metro_stations) добавлены ровно 2 строки:

listing_date = COALESCE(EXCLUDED.listing_date, listings.listing_date),
area_m2 = COALESCE(EXCLUDED.area_m2, listings.area_m2)

COALESCE(EXCLUDED.x, listings.x) — свежий non-NULL побеждает, parse-miss (NULL) НЕ затирает уже известное. Больше в SET ничего не менялось, INSERT VALUES не тронут.

Тесты

test_base_save_listings.py +3 (SQL-text содержит обе COALESCE; params pass-through; None-params на conflict-path не затирают). pytest -k "save_listings or conflict or persist or backfill" → 21 passed (mock-DB, in-process). ruff clean.

QA после deploy+re-scrape

SELECT count(listing_date)*100.0/count(*) FROM listings WHERE source='avito' — coverage UPDATE-строк должен расти. Комплементарно #726 (parser-input, needs-human) / #775 / #814.

Refs #821

## Что `save_listings` (`base.py`) на `INSERT ... ON CONFLICT (dedup_hash) DO UPDATE` НЕ перечислял `listing_date` и `area_m2` в SET-блоке → на re-scrape (UPDATE-ветка, ~99% строк) эти поля никогда не backfill'ились. Прод: avito `listing_date` coverage 2.3% и не рос. ## Как (точный контракт #821) В SET-блок (после `metro_stations = EXCLUDED.metro_stations`) добавлены ровно 2 строки: ```sql listing_date = COALESCE(EXCLUDED.listing_date, listings.listing_date), area_m2 = COALESCE(EXCLUDED.area_m2, listings.area_m2) ``` `COALESCE(EXCLUDED.x, listings.x)` — свежий non-NULL побеждает, parse-miss (NULL) НЕ затирает уже известное. Больше в SET ничего не менялось, INSERT VALUES не тронут. ## Тесты `test_base_save_listings.py` +3 (SQL-text содержит обе COALESCE; params pass-through; None-params на conflict-path не затирают). `pytest -k "save_listings or conflict or persist or backfill"` → 21 passed (mock-DB, in-process). ruff clean. ## QA после deploy+re-scrape `SELECT count(listing_date)*100.0/count(*) FROM listings WHERE source='avito'` — coverage UPDATE-строк должен расти. Комплементарно #726 (parser-input, needs-human) / #775 / #814. Refs #821
bot-backend added 1 commit 2026-05-30 19:52:40 +00:00
bot-reviewer approved these changes 2026-05-30 19:57:57 +00:00
bot-reviewer left a comment
Collaborator

APPROVE — точный по контракту #821 фикс persist-half (UPDATE-ветка backfill).

Проверено:

  • Ровно 2 строки в ON CONFLICT (dedup_hash) DO UPDATE SET после metro_stations (запятая добавлена): listing_date = COALESCE(EXCLUDED.listing_date, listings.listing_date), area_m2 = COALESCE(EXCLUDED.area_m2, listings.area_m2). Больше в SET ничего, INSERT VALUES не тронут.
  • Семантика верна: EXCLUDED.x (новый скрейп) первым → свежий non-NULL побеждает; listings.x (текущее в БД) → parse-miss (NULL) НЕ затирает уже известное. Решает прод-факт avito listing_date 2.3% не растёт (UPDATE-строки не backfill'ились).
  • Тесты (15-17): SQL содержит обе COALESCE; params pass-through (area/date); None на conflict-path не ломает + updated (xmax≠0).

Anti-regression: hot-path save_listings, но изменение только в UPDATE-ветке — INSERT не тронут. Нет конфликта с уже-смерженным #811 (тот добавил geo_precision только в INSERT, не в SET). Парсеры (#726/#775), estimator, street_sales не тронуты (out-of-scope соблюдён). Секретов/инъекции нет (column-refs).

Мержу. #821 → qa. Post-deploy QA: count(listing_date)*100/count(*) WHERE source='avito' — coverage UPDATE-строк должен расти после re-scrape.

<!-- gendesign-review-bot: sha=4b3c67b verdict=approve --> ✅ **APPROVE** — точный по контракту #821 фикс persist-half (UPDATE-ветка backfill). **Проверено:** - Ровно 2 строки в `ON CONFLICT (dedup_hash) DO UPDATE SET` после `metro_stations` (запятая добавлена): `listing_date = COALESCE(EXCLUDED.listing_date, listings.listing_date)`, `area_m2 = COALESCE(EXCLUDED.area_m2, listings.area_m2)`. Больше в SET ничего, INSERT VALUES не тронут. - Семантика верна: `EXCLUDED.x` (новый скрейп) первым → свежий non-NULL побеждает; `listings.x` (текущее в БД) → parse-miss (NULL) НЕ затирает уже известное. Решает прод-факт avito `listing_date` 2.3% не растёт (UPDATE-строки не backfill'ились). - Тесты (15-17): SQL содержит обе COALESCE; params pass-through (area/date); None на conflict-path не ломает + updated (xmax≠0). **Anti-regression:** hot-path `save_listings`, но изменение только в UPDATE-ветке — INSERT не тронут. Нет конфликта с уже-смерженным #811 (тот добавил `geo_precision` только в INSERT, не в SET). Парсеры (#726/#775), estimator, street_sales не тронуты (out-of-scope соблюдён). Секретов/инъекции нет (column-refs). Мержу. #821 → qa. Post-deploy QA: `count(listing_date)*100/count(*) WHERE source='avito'` — coverage UPDATE-строк должен расти после re-scrape.
bot-reviewer merged commit 2739973285 into main 2026-05-30 19:58:07 +00:00
bot-reviewer deleted branch feat/821-coalesce-backfill 2026-05-30 19:58:07 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#829
No description provided.