"""Search endpoint for semantic + keyword search with graceful fallback.""" from __future__ import annotations import logging from typing import Annotated from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession from config import get_settings from database import get_session from schemas import SearchResponse, SearchResultItem from search_service import SearchService logger = logging.getLogger("chrysopedia.search.router") router = APIRouter(prefix="/search", tags=["search"]) def _get_search_service() -> SearchService: """Build a SearchService from current settings.""" return SearchService(get_settings()) @router.get("", response_model=SearchResponse) async def search( q: Annotated[str, Query(max_length=500)] = "", scope: Annotated[str, Query()] = "all", limit: Annotated[int, Query(ge=1, le=100)] = 20, db: AsyncSession = Depends(get_session), ) -> SearchResponse: """Semantic search with keyword fallback. - **q**: Search query (max 500 chars). Empty → empty results. - **scope**: ``all`` | ``topics`` | ``creators``. Invalid → defaults to ``all``. - **limit**: Max results (1–100, default 20). """ svc = _get_search_service() result = await svc.search(query=q, scope=scope, limit=limit, db=db) return SearchResponse( items=[SearchResultItem(**item) for item in result["items"]], total=result["total"], query=result["query"], fallback_used=result["fallback_used"], )