gendesign/tradein-mvp/backend/app/schemas/search_response.py
lekss361 9643979d2c
All checks were successful
Deploy Trade-In / deploy (push) Successful in 26s
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 56s
feat(tradein): Phase 3.2 — /api/v1/search endpoint + Redis cache (#479)
POST /api/v1/search reading listings_search_mv with ~20 cross-source filters, parameterized SQL (CAST(:x AS type), psycopg v3), whitelisted ORDER BY (Pydantic Literal), Redis 5min hot cache with graceful degradation (singleton pool via lru_cache).

Verified vs data/sql/050_search_optimization.sql: matview column refs (total_area, lng, house_rating, sources[], has_avito/cian/yandex), SQL injection safety, cache failure swallow, router prefix.

Deep-code-reviewer: APPROVE.
2026-05-23 14:44:22 +00:00

53 lines
1.3 KiB
Python

"""SearchResponse + SearchResultItem — /api/v1/search return shape."""
from __future__ import annotations
from datetime import datetime
from pydantic import BaseModel, ConfigDict
class SearchResultItem(BaseModel):
model_config = ConfigDict(from_attributes=True)
listing_id: int
source: str
source_url: str | None = None
address: str | None = None
lat: float | None = None
lng: float | None = None
rooms: int | None = None
total_area: float | None = None
floor: int | None = None
total_floors: int | None = None
price_rub: int | None = None
price_per_m2: int | None = None
kadastr_num: str | None = None
scraped_at: datetime | None = None
house_id: int | None = None
year_built: int | None = None
house_class: str | None = None
developer_name: str | None = None
house_rating: float | None = None
house_ratings_count: int | None = None
source_count: int | None = None
sources: list[str] | None = None
has_avito: bool | None = None
has_cian: bool | None = None
has_yandex: bool | None = None
house_median_ppm2: float | None = None
district: str | None = None
class SearchResponse(BaseModel):
items: list[SearchResultItem]
total: int
page: int
page_size: int
elapsed_ms: float | None = None
cache_hit: bool = False