"""Partial index for the photos list query

Revision ID: 0017_photos_list_index
Revises: 0016_nextcloud_integration
Create Date: 2026-05-10

The default photos-list query (GET /api/v1/photos?per_page=N&sort=taken_at&order=desc)
filters `NOT is_trashed AND NOT is_hidden` and sorts by
`(taken_at DESC NULLS LAST, id DESC)`. EXPLAIN ANALYZE on a 21k-row
table showed a seq-scan + top-N heapsort (~20ms standalone, worse under
concurrency) — Postgres can't use the single-column ix_photos_taken_at
when the leading WHERE clause is two booleans.

A partial index on the sort key, scoped to the visible subset, lets the
planner index-scan in reverse and stop at LIMIT N. Two booleans select
~95% of rows, so the partial predicate is tighter than the full table
without losing common queries.
"""
from typing import Sequence, Union

from alembic import op

revision: str = "0017_photos_list_index"
down_revision: Union[str, None] = "0016_nextcloud_integration"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    op.execute(
        """
        CREATE INDEX IF NOT EXISTS ix_photos_list_visible
        ON photos (taken_at DESC NULLS LAST, id DESC)
        WHERE NOT is_trashed AND NOT is_hidden
        """
    )


def downgrade() -> None:
    op.execute("DROP INDEX IF EXISTS ix_photos_list_visible")
