"""Plex: make cast/crew names searchable Revision ID: 0046_plex_people_search Revises: 0045_plex_filter_meta Create Date: 2026-07-06 Adds `people_text` (the item's cast + director names, space-joined) and folds it into the generated `search_vector` at weight B — between the title (A) and the summary (C). So the top search box now finds titles by an actor/director name and ranks those above films that merely mention the name in their synopsis. A generated column's expression can't be altered in place, so we drop + recreate it (and its GIN index). `people_text` is populated on the next Plex sync. """ from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "0046_plex_people_search" down_revision: Union[str, None] = "0045_plex_filter_meta" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None _CFG = "public.unaccent_simple" _NEW_VECTOR = ( f"setweight(to_tsvector('{_CFG}', coalesce(title, '')), 'A') || " f"setweight(to_tsvector('{_CFG}', coalesce(people_text, '')), 'B') || " f"setweight(to_tsvector('{_CFG}', left(coalesce(summary, ''), 1000)), 'C')" ) _OLD_VECTOR = ( f"setweight(to_tsvector('{_CFG}', coalesce(title, '')), 'A') || " f"setweight(to_tsvector('{_CFG}', left(coalesce(summary, ''), 1000)), 'C')" ) def _rebuild_vector(expr: str) -> None: op.execute("DROP INDEX IF EXISTS ix_plex_items_search_vector") op.execute("ALTER TABLE plex_items DROP COLUMN search_vector") op.execute( f"ALTER TABLE plex_items ADD COLUMN search_vector tsvector " f"GENERATED ALWAYS AS ({expr}) STORED" ) op.execute("CREATE INDEX ix_plex_items_search_vector ON plex_items USING gin (search_vector)") def upgrade() -> None: op.add_column("plex_items", sa.Column("people_text", sa.Text(), nullable=True)) _rebuild_vector(_NEW_VECTOR) def downgrade() -> None: _rebuild_vector(_OLD_VECTOR) op.drop_column("plex_items", "people_text")