API: поиск игроков по нику для автокомплита (GET /api/users/search)
До 10 подсказок по подстроке ника среди активных игроков (role=player). Регистронезависимо и для кириллицы: SQLite LIKE/lower сворачивают регистр только у ASCII, поэтому фильтрация/ранжирование в Python (casefold): префиксные совпадения раньше подстрочных, внутри — по алфавиту. Пустой q — пустой список. Тесты + регенерированный schema.d.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,28 @@ def nickname_available(session: Session, nickname: str, exclude_user_id: int | N
|
||||
return existing is None or existing.id == exclude_user_id
|
||||
|
||||
|
||||
def search_users(session: Session, query: str, limit: int = 3) -> list[User]:
|
||||
"""Подсказки по нику: активные игроки (role='player'), без учёта регистра.
|
||||
|
||||
SQLite LIKE/lower() сворачивают регистр только для ASCII, а ники бывают
|
||||
кириллицей — поэтому выбираем активных игроков и фильтруем/ранжируем в
|
||||
Python (casefold). Пользователей десятки, полный проход дёшев; при росте
|
||||
до тысяч — заменить на индексированную колонку nickname_lower.
|
||||
Сначала префиксные совпадения, затем подстрочные, внутри — по алфавиту.
|
||||
"""
|
||||
q = (query or "").strip().casefold()
|
||||
if not q:
|
||||
return []
|
||||
users = session.exec(
|
||||
select(User).where(User.role == "player", User.is_active == True) # noqa: E712
|
||||
).all()
|
||||
matched = [u for u in users if q in u.nickname.casefold()]
|
||||
matched.sort(
|
||||
key=lambda u: (0 if u.nickname.casefold().startswith(q) else 1, u.nickname.casefold())
|
||||
)
|
||||
return matched[:limit]
|
||||
|
||||
|
||||
def nickname_format_ok(nickname: str) -> bool:
|
||||
"""Ник подходит по формату (2–64, буквы/цифры/пробел/.-_)."""
|
||||
return bool(_NICK_RE.match((nickname or "").strip()))
|
||||
|
||||
Reference in New Issue
Block a user