feat(db): schema user_weight_profiles (#114 sub-PR 1/4 — foundation) #136

Merged
lekss361 merged 1 commit from feat/114-user-weight-profiles-schema into main 2026-05-14 21:19:33 +00:00
lekss361 commented 2026-05-14 21:16:11 +00:00 (Migrated from github.com)

Summary

Foundation для #114 Custom POI weights (Макс feedback "садики ≠ Мегамарт"). Sub-PR 1 of 4 per feedback_split_big_issues:

  1. This — schema migration
  2. Next — backend Pydantic models + CRUD service
  3. After — POST /analyze?profile= override + admin endpoint
  4. Last — frontend 25 sliders UI + live preview

Schema

data/sql/90_user_weight_profiles.sql:

Object Type Purpose
user_weight_profiles TABLE Stored profiles per user
weights JSONB {poi_category: weight} map
(user_id, profile_name) UNIQUE Profile uniqueness per user
user_idx INDEX Fetch profiles by user_id
default_idx PARTIAL INDEX WHERE is_default=TRUE (fast lookup)
user_weight_profiles_updated_at TRIGGER Auto-bump updated_at on UPDATE
set_updated_at() FUNCTION Trigger body, OR REPLACE

Compat

JSONB compatible с _POI_WEIGHTS const (parcels.py):

  • school/kindergarten/pharmacy/hospital
  • shop_mall/shop_supermarket/shop_small
  • park/bus_stop/metro_stop/tram_stop

Idempotency

  • BEGIN/COMMIT
  • IF NOT EXISTS / OR REPLACE / DROP+CREATE TRIGGER

Apply на prod после merge

psql ... -f data/sql/90_user_weight_profiles.sql

Inert после apply — backend код добавит sub-PR 2.

Vault

code/schemas/Schema_User_Weight_Profiles.md (NEW, full sub-PR plan).

Refs: #114

## Summary Foundation для **#114 Custom POI weights** (Макс feedback "садики ≠ Мегамарт"). Sub-PR 1 of 4 per `feedback_split_big_issues`: 1. **This** — schema migration 2. Next — backend Pydantic models + CRUD service 3. After — POST /analyze?profile= override + admin endpoint 4. Last — frontend 25 sliders UI + live preview ## Schema `data/sql/90_user_weight_profiles.sql`: | Object | Type | Purpose | |---|---|---| | `user_weight_profiles` | TABLE | Stored profiles per user | | `weights` | JSONB | `{poi_category: weight}` map | | `(user_id, profile_name)` | UNIQUE | Profile uniqueness per user | | `user_idx` | INDEX | Fetch profiles by user_id | | `default_idx` | PARTIAL INDEX | `WHERE is_default=TRUE` (fast lookup) | | `user_weight_profiles_updated_at` | TRIGGER | Auto-bump updated_at on UPDATE | | `set_updated_at()` | FUNCTION | Trigger body, OR REPLACE | ## Compat JSONB compatible с `_POI_WEIGHTS` const (parcels.py): - school/kindergarten/pharmacy/hospital - shop_mall/shop_supermarket/shop_small - park/bus_stop/metro_stop/tram_stop ## Idempotency - BEGIN/COMMIT - IF NOT EXISTS / OR REPLACE / DROP+CREATE TRIGGER ## Apply на prod после merge ```bash psql ... -f data/sql/90_user_weight_profiles.sql ``` Inert после apply — backend код добавит sub-PR 2. ## Vault `code/schemas/Schema_User_Weight_Profiles.md` (NEW, full sub-PR plan). Refs: #114
lekss361 commented 2026-05-14 21:19:19 +00:00 (Migrated from github.com)

Bot review (SHA 5d87050, CI )

Чистая foundation для #114. Schema-only PR, inert до sub-PR 2.

Design

Idempotent — BEGIN/COMMIT, IF NOT EXISTS для table/index, OR REPLACE для function, DROP+CREATE для trigger
UNIQUE(user_id, profile_name) — prevents duplicate profile names per user
Partial index user_weight_profiles_default_idx WHERE is_default=TRUE — efficient default-profile lookup (особенно при много NULL/FALSE rows)
set_updated_at() с OR REPLACE — reusable trigger function для будущих таблиц; conflicting determinist если кто-то реальзует такую же — OR REPLACE корректно перезапишет
JSONB weights совместим с _POI_WEIGHTS в backend/app/api/v1/parcels.py (school, kindergarten, pharmacy, hospital, shop_mall/supermarket/small, park, bus_stop, metro_stop, tram_stop)
Apply order правильный — 90 после 89_drop_brin
COMMENT ON TABLE/COLUMN — documentation сохранится в pg_dump, помогает следующим разработчикам

Минор (не блокер, явно отложено)

user_id TEXT NOT NULL без FK на users-таблицу. В COMMENT упомянуто 'Full auth integration — Sprint 5'. Acceptable для foundation PR.

approve merge

## Bot review (SHA `5d87050`, CI ✅) Чистая foundation для #114. Schema-only PR, inert до sub-PR 2. ### Design ✅ **Idempotent** — BEGIN/COMMIT, `IF NOT EXISTS` для table/index, `OR REPLACE` для function, `DROP+CREATE` для trigger ✅ **`UNIQUE(user_id, profile_name)`** — prevents duplicate profile names per user ✅ **Partial index `user_weight_profiles_default_idx WHERE is_default=TRUE`** — efficient default-profile lookup (особенно при много NULL/FALSE rows) ✅ **`set_updated_at()` с `OR REPLACE`** — reusable trigger function для будущих таблиц; conflicting determinist если кто-то реальзует такую же — `OR REPLACE` корректно перезапишет ✅ **JSONB weights** совместим с `_POI_WEIGHTS` в `backend/app/api/v1/parcels.py` (school, kindergarten, pharmacy, hospital, shop_mall/supermarket/small, park, bus_stop, metro_stop, tram_stop) ✅ **Apply order правильный** — 90 после 89_drop_brin ✅ **`COMMENT ON TABLE/COLUMN`** — documentation сохранится в `pg_dump`, помогает следующим разработчикам ### Минор (не блокер, явно отложено) `user_id TEXT NOT NULL` без FK на users-таблицу. В COMMENT упомянуто `'Full auth integration — Sprint 5'`. Acceptable для foundation PR. approve merge
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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#136
No description provided.