feat(search): pg_trgm-Suchindex + Backend-Endpunkt statt Client-Filter
CI / backend-tests (push) Failing after 2m9s
CI / frontend-build (push) Successful in 21s

Command Palette lud bisher beim Öffnen ALLE Objekte und filterte nur
clientseitig per .includes() - unscharf und würde bei mehr Objekten
langsam werden. Jetzt: GIN-Trigram-Index auf objekt.name/code (Migration
0023), neuer GET /suche?q=-Endpunkt (Ähnlichkeits-Ranking per pg_trgm
similarity()), Frontend debounced (250ms) statt vollständiger Objektliste.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-08 00:44:03 +02:00
co-authored by Claude Sonnet 5
parent c608f06806
commit 307914ccf7
7 changed files with 147 additions and 18 deletions
+2
View File
@@ -20,6 +20,7 @@ from app.api.v1.endpoints import (
permission,
personal,
stammdaten,
suche,
vorlagen,
wartung,
zustaendigkeit,
@@ -48,3 +49,4 @@ api_router.include_router(akte.router, tags=["akte"])
api_router.include_router(lager.router, tags=["lager"])
api_router.include_router(ausgabe.router, tags=["ausgabe"])
api_router.include_router(wartung.router, tags=["wartung"])
api_router.include_router(suche.router, tags=["suche"])
+38
View File
@@ -0,0 +1,38 @@
from fastapi import APIRouter, Depends
from sqlalchemy import func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user
from app.db.session import get_db
from app.models.objekt import Objekt
from app.schemas.suche import SuchTreffer
router = APIRouter()
_TREFFER_LIMIT = 8
@router.get("/suche", response_model=list[SuchTreffer])
async def suche(
q: str,
db: AsyncSession = Depends(get_db),
_=Depends(get_current_user),
) -> list[SuchTreffer]:
"""Globale Suche (Command Palette) über Objektname/-code, per pg_trgm-
Index statt vollständig geladener Objektliste + Client-Filter (SEARCH-001).
Kurze Eingaben (<2 Zeichen) liefern bewusst nichts - sonst zu viele
Zufallstreffer über den Ähnlichkeits-Schwellenwert von pg_trgm."""
q = q.strip()
if len(q) < 2:
return []
muster = f"%{q}%"
aehnlichkeit = func.greatest(func.similarity(Objekt.name, q), func.similarity(Objekt.code, q))
stmt = (
select(Objekt.id, Objekt.code, Objekt.name)
.where(or_(Objekt.name.ilike(muster), Objekt.code.ilike(muster), aehnlichkeit > 0.2))
.order_by(aehnlichkeit.desc())
.limit(_TREFFER_LIMIT)
)
result = await db.execute(stmt)
return [SuchTreffer(id=r.id, code=r.code, name=r.name) for r in result.all()]