53 lines
1.4 KiB
Python
53 lines
1.4 KiB
Python
import database
|
|
"""Stock search endpoint — GET /api/v1/search."""
|
|
|
|
import logging
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Query
|
|
|
|
from database import execute_query
|
|
from schemas.stock import SearchResult
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix="/search", tags=["stocks"])
|
|
|
|
|
|
@router.get(
|
|
"",
|
|
summary="Search stocks by ticker or name",
|
|
response_model=list[SearchResult],
|
|
responses={400: {"description": "Invalid search query"}},
|
|
)
|
|
async def search_stocks(
|
|
q: str = Query(..., min_length=1, max_length=50, description="Search query"),
|
|
limit: int = Query(default=10, ge=1, le=50),
|
|
):
|
|
"""Search stocks by ticker symbol or company name.
|
|
|
|
Uses full-text search across stock_profiles table.
|
|
"""
|
|
like_pattern = f"{q}%"
|
|
sql = """
|
|
SELECT ticker, name, exchange, sector, market_cap
|
|
FROM stock_profiles
|
|
WHERE ticker ILIKE $1 OR name ILIKE $2
|
|
ORDER BY
|
|
CASE WHEN ticker ILIKE $1 THEN 0 ELSE 1 END,
|
|
name
|
|
LIMIT $3
|
|
"""
|
|
rows = await database.execute_query(sql, (like_pattern, f"%{q}%", limit))
|
|
|
|
results = []
|
|
for row in rows:
|
|
results.append(SearchResult(
|
|
ticker=row["ticker"],
|
|
name=row["name"],
|
|
exchange=row["exchange"],
|
|
sector=row["sector"],
|
|
marketCap=float(row["market_cap"]) if row["market_cap"] else None,
|
|
))
|
|
|
|
return results
|