Files
ai_web/backend/config/search.py
2026-01-28 16:00:56 +08:00

64 lines
1.9 KiB
Python

"""
Global search API routes.
"""
from typing import List
from ninja import Router, Schema
from django.db.models import Count, Q
from django.conf import settings
from django.views.decorators.cache import cache_page
from apps.products.models import Product, Website
from apps.products.schemas import ProductOut, WebsiteOut
from apps.bounties.models import Bounty
from apps.bounties.schemas import BountyWithDetailsOut
from apps.common.serializers import serialize_bounty
router = Router()
class SearchResultsOut(Schema):
products: List[ProductOut]
websites: List[WebsiteOut]
bounties: List[BountyWithDetailsOut]
def _serialize_bounty_with_counts(bounty):
return serialize_bounty(bounty, include_counts=True)
@router.get("/", response=SearchResultsOut)
@cache_page(settings.CACHE_TTL_SECONDS)
def global_search(request, q: str, limit: int = 10):
"""Search products, websites and bounties by keyword."""
keyword = (q or "").strip()
if not keyword:
return SearchResultsOut(products=[], websites=[], bounties=[])
products = list(
Product.objects.select_related("category").filter(
Q(name__icontains=keyword) | Q(description__icontains=keyword)
).order_by("-created_at")[:limit]
)
websites = list(
Website.objects.select_related("category").filter(
Q(name__icontains=keyword) | Q(description__icontains=keyword)
).order_by("-created_at")[:limit]
)
bounties = list(
Bounty.objects.select_related("publisher", "acceptor")
.annotate(
applications_count=Count("applications", distinct=True),
comments_count=Count("comments", distinct=True),
)
.filter(Q(title__icontains=keyword) | Q(description__icontains=keyword))
.order_by("-created_at")[:limit]
)
return SearchResultsOut(
products=products,
websites=websites,
bounties=[_serialize_bounty_with_counts(b) for b in bounties],
)