fix(performers): szukanie po aliasach i w odwrotnej kolejnosci slow

Zgloszenie 436121fb: "Takase Rina" (japonska kolejnosc) nie znajdowalo
"Rina Takase" (15 scen) i wygladalo, jakby jej nie bylo. Wyszukiwanie
patrzy teraz na podciag nazwy albo aliasu, dla dwoch slow tez w odwrotnej
kolejnosci; trafienia w nazwie ida przed trafieniami tylko w aliasie.
Obie kolumny maja indeksy trigramowe, 0,2 do 1,1 s w sandboksie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
goon-foss 2026-10-12 09:56:59 +02:00
parent ce174b671e
commit cd34b296e2

View file

@ -15,14 +15,14 @@ from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
from sqlalchemy import and_, exists, func, select from sqlalchemy import and_, case, exists, func, or_, select
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.auth import require_api_key from app.auth import require_api_key
from app.db import get_session from app.db import get_session
from app.models.movie import Movie, MovieTag from app.models.movie import Movie, MovieTag
from app.models.movie_playback_source import MoviePlaybackSource from app.models.movie_playback_source import MoviePlaybackSource
from app.models.performer import Performer from app.models.performer import Performer, PerformerAlias
from app.models.playback_source import PlaybackSource from app.models.playback_source import PlaybackSource
from app.models.scene import ScenePerformer, SceneTag from app.models.scene import ScenePerformer, SceneTag
from app.models.studio import Studio from app.models.studio import Studio
@ -30,6 +30,37 @@ from app.models.tag import Tag
from app.models.tag_vertical_count import TagVerticalCount from app.models.tag_vertical_count import TagVerticalCount
from app.verticals import VERTICALS from app.verticals import VERTICALS
def _performer_name_patterns(q: str) -> list[str]:
qn = " ".join(q.lower().split())
pats = [f"%{qn}%"]
words = qn.split(" ")
if len(words) == 2:
pats.append(f"%{words[1]} {words[0]}%")
return pats
def _performer_name_rank(q: str):
"""0 = trafienie w nazwie, 1 = tylko w aliasie; sortujemy po tym przed liczba scen,
inaczej "anna" dawalo na gorze duze nazwiska z aliasem "Adrianna"."""
return case((or_(*[Performer.name_normalized.ilike(p) for p in _performer_name_patterns(q)]), 0), else_=1)
def _performer_name_clause(q: str):
"""Szukanie wykonawcy: podciag nazwy ALBO aliasu, a dla dwoch slow tez w odwrotnej
kolejnosci. Zgloszenie 436121fb: "Takase Rina" (japonska kolejnosc) nie znajdowalo
"Rina Takase" i wygladalo, jakby jej nie bylo. Obie kolumny maja indeksy trigramowe."""
pats = _performer_name_patterns(q)
return or_(
*[Performer.name_normalized.ilike(p) for p in pats],
exists(
select(1).where(
PerformerAlias.performer_id == Performer.id,
or_(*[PerformerAlias.alias_normalized.ilike(p) for p in pats]),
)
),
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
router = APIRouter(tags=["taxonomies"], dependencies=[Depends(require_api_key)]) router = APIRouter(tags=["taxonomies"], dependencies=[Depends(require_api_key)])
@ -294,14 +325,15 @@ def list_performers(
.join(v_count, v_count.c.performer_id == Performer.id) .join(v_count, v_count.c.performer_id == Performer.id)
) )
if q: if q:
base = base.where(Performer.name_normalized.ilike(f"%{q.lower()}%")) base = base.where(_performer_name_clause(q))
total = session.execute( total = session.execute(
select(func.count()).select_from(base.subquery()) select(func.count()).select_from(base.subquery())
).scalar_one() ).scalar_one()
rank = [_performer_name_rank(q)] if q else []
if order in ("scene_count", "popular"): if order in ("scene_count", "popular"):
ordered = base.order_by(v_count.c.c.desc(), Performer.canonical_name.asc()) ordered = base.order_by(*rank, v_count.c.c.desc(), Performer.canonical_name.asc())
else: else:
ordered = base.order_by(Performer.canonical_name.asc()) ordered = base.order_by(*rank, Performer.canonical_name.asc())
rows = session.execute( rows = session.execute(
ordered.offset((page - 1) * per_page).limit(per_page) ordered.offset((page - 1) * per_page).limit(per_page)
).all() ).all()
@ -323,18 +355,19 @@ def list_performers(
# 3M scene_performers + EXISTS playback per-request. Patrz migracja 0019. # 3M scene_performers + EXISTS playback per-request. Patrz migracja 0019.
base = select(Performer) base = select(Performer)
if q: if q:
base = base.where(Performer.name_normalized.ilike(f"%{q.lower()}%")) base = base.where(_performer_name_clause(q))
total = session.execute( total = session.execute(
select(func.count()).select_from(base.subquery()) select(func.count()).select_from(base.subquery())
).scalar_one() ).scalar_one()
rank = [_performer_name_rank(q)] if q else []
if order in ("scene_count", "popular"): if order in ("scene_count", "popular"):
ordered = base.order_by( ordered = base.order_by(
Performer.scene_count.desc(), Performer.canonical_name.asc() *rank, Performer.scene_count.desc(), Performer.canonical_name.asc()
) )
else: else:
ordered = base.order_by(Performer.canonical_name.asc()) ordered = base.order_by(*rank, Performer.canonical_name.asc())
perfs_page = session.execute( perfs_page = session.execute(
ordered.offset((page - 1) * per_page).limit(per_page) ordered.offset((page - 1) * per_page).limit(per_page)