Initial project commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy import func, or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_db
|
||||
from app.models.spot import Spot
|
||||
from app.models.tag import SpotTag, Tag
|
||||
from app.schemas.common import PageResponse
|
||||
from app.schemas.spot import SpotBrief
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _spot_to_brief(spot: Spot) -> SpotBrief:
|
||||
cover = next((img for img in spot.images if img.is_cover), None)
|
||||
if cover is None and spot.images:
|
||||
cover = spot.images[0]
|
||||
return SpotBrief(
|
||||
id=spot.id,
|
||||
title=spot.title,
|
||||
city=spot.city,
|
||||
longitude=spot.longitude,
|
||||
latitude=spot.latitude,
|
||||
cover_image_url=cover.image_url if cover else None,
|
||||
audit_status=spot.audit_status,
|
||||
avg_rating=spot.avg_rating,
|
||||
created_at=spot.created_at,
|
||||
)
|
||||
|
||||
|
||||
@router.get("", response_model=PageResponse[SpotBrief])
|
||||
async def search_spots(
|
||||
q: str = Query(..., min_length=1),
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
if not q.strip():
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Query cannot be empty")
|
||||
|
||||
pattern = f"%{q}%"
|
||||
|
||||
tag_spot_ids = (
|
||||
select(SpotTag.spot_id)
|
||||
.join(Tag, SpotTag.tag_id == Tag.id)
|
||||
.where(Tag.name.ilike(pattern))
|
||||
.distinct()
|
||||
.correlate(None)
|
||||
.scalar_subquery()
|
||||
)
|
||||
|
||||
filters = (
|
||||
Spot.audit_status == "approved",
|
||||
or_(
|
||||
Spot.title.ilike(pattern),
|
||||
Spot.description.ilike(pattern),
|
||||
Spot.id.in_(tag_spot_ids),
|
||||
),
|
||||
)
|
||||
|
||||
count_result = await db.execute(select(func.count(Spot.id)).where(*filters))
|
||||
total = count_result.scalar() or 0
|
||||
|
||||
offset = (page - 1) * page_size
|
||||
result = await db.execute(
|
||||
select(Spot)
|
||||
.where(*filters)
|
||||
.order_by(Spot.created_at.desc())
|
||||
.offset(offset)
|
||||
.limit(page_size)
|
||||
)
|
||||
spots = result.scalars().all()
|
||||
|
||||
return PageResponse(total=total, items=[_spot_to_brief(s) for s in spots])
|
||||
Reference in New Issue
Block a user