"""image_record.siglip_embedding: HNSW cosine index for "more like this"

Revision ID: 0036
Revises: 0035
Create Date: 2026-06-04

Gallery Phase 3 (visual similarity search) ranks images by
`siglip_embedding.cosine_distance(source_embedding)`. Without an index that's
a sequential scan computing a 1152-dim distance for every row — fine at small
scale, but it grows linearly with the library. Add an HNSW index with
`vector_cosine_ops` so the top-N nearest search is sub-50ms ANN.

1152 dims is under pgvector's 2000-dim HNSW limit, so HNSW (no training,
better recall than IVFFlat) is the right choice. ONE-TIME COST: building the
index over the existing embeddings (~57k vectors on the operator's library)
locks image_record for ~30-60s during this migration on deploy — acceptable
for a single-operator homelab. NULL embeddings (videos / not-yet-embedded
rows) are simply not indexed.
"""
from typing import Sequence, Union

from alembic import op

revision: str = "0036"
down_revision: Union[str, None] = "0035"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # Raw SQL: alembic's create_index doesn't express the `USING hnsw (...
    # vector_cosine_ops)` access-method + opclass cleanly. Must match the
    # query's cosine_distance operator class to be usable by the planner.
    op.execute(
        "CREATE INDEX ix_image_record_siglip_hnsw "
        "ON image_record USING hnsw (siglip_embedding vector_cosine_ops)"
    )


def downgrade() -> None:
    op.drop_index("ix_image_record_siglip_hnsw", table_name="image_record")
