"""initial schema Revision ID: 0001_initial_schema Revises: Create Date: 2026-09-04 Creates every Aurora Core 0.1 table (section 26/33 of the build spec): reference data (languages/countries/certifications), providers and provenance, genres/keywords, companies, collections, people, movies and their satellite tables, series/seasons/episodes and their satellite tables, and the polymorphic credit/artwork/video/external-id tables. Also enables `pg_trgm` and creates the functional GIN indexes PostgreSQLSearchProvider relies on (section 16). """ from __future__ import annotations from collections.abc import Sequence import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql revision: str = "0001_initial_schema" down_revision: str | None = None branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None def _timestamps() -> list[sa.Column]: return [ sa.Column( "created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False, ), sa.Column( "updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False, ), ] # create_type=False on every enum below: their CREATE TYPE / DROP TYPE DDL # is issued explicitly in upgrade()/downgrade() instead. Without this, # SQLAlchemy also tries to auto-create each type as a side effect of the # first `op.create_table()` that uses a column of that type, colliding with # the explicit `enum_type.create(...)` call and raising DuplicateObject. MOVIE_STATUS = postgresql.ENUM( "rumored", "planned", "in_production", "post_production", "released", "canceled", name="moviestatus_enum", create_type=False, ) SERIES_STATUS = postgresql.ENUM( "planned", "pilot", "in_production", "returning_series", "ended", "canceled", name="seriesstatus_enum", create_type=False, ) ARTWORK_TYPE = postgresql.ENUM( "poster", "backdrop", "logo", "profile", "still", "banner", "thumbnail", name="artworktype_enum", create_type=False, ) VIDEO_TYPE = postgresql.ENUM( "trailer", "teaser", "clip", "featurette", "behind_the_scenes", "interview", "other", name="videotype_enum", create_type=False, ) COMPANY_TYPE = postgresql.ENUM( "production_company", "network", "studio", "distributor", name="companytype_enum", create_type=False, ) INGESTION_JOB_STATUS = postgresql.ENUM( "pending", "running", "succeeded", "failed", "retrying", name="ingestionjobstatus_enum", create_type=False, ) MATCH_CONFIDENCE = postgresql.ENUM( "exact", "high", "medium", "low", "ambiguous", "new", name="matchconfidence_enum", create_type=False, ) def upgrade() -> None: bind = op.get_bind() op.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm") for enum_type in ( MOVIE_STATUS, SERIES_STATUS, ARTWORK_TYPE, VIDEO_TYPE, COMPANY_TYPE, INGESTION_JOB_STATUS, MATCH_CONFIDENCE, ): enum_type.create(bind, checkfirst=True) # --- Reference data ----------------------------------------------- op.create_table( "languages", sa.Column("code", sa.String(10), primary_key=True), sa.Column("english_name", sa.String(200), nullable=False), sa.Column("native_name", sa.String(200), nullable=True), ) op.create_table( "countries", sa.Column("code", sa.String(2), primary_key=True), sa.Column("name", sa.String(200), nullable=False), ) op.create_table( "certifications", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("country_code", sa.String(2), sa.ForeignKey("countries.code"), nullable=False), sa.Column("system", sa.String(50), nullable=False), sa.Column("rating", sa.String(20), nullable=False), sa.Column("meaning", sa.Text(), nullable=True), sa.UniqueConstraint("country_code", "system", "rating", name="uq_certification_scope"), ) # --- Providers & provenance ----------------------------------------- op.create_table( "providers", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("slug", sa.String(50), nullable=False, unique=True), sa.Column("name", sa.String(200), nullable=False), sa.Column("base_url", sa.String(500), nullable=True), sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.true()), sa.Column( "config", postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default="{}" ), ) op.create_table( "ingestion_jobs", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column( "provider_id", sa.Uuid(), sa.ForeignKey("providers.id", ondelete="CASCADE"), nullable=False, ), sa.Column("job_type", sa.String(50), nullable=False), sa.Column("entity_type", sa.String(30), nullable=True), sa.Column("external_id", sa.String(200), nullable=True), sa.Column("idempotency_key", sa.String(300), nullable=False), sa.Column("status", INGESTION_JOB_STATUS, nullable=False, server_default="pending"), sa.Column("attempts", sa.Integer(), nullable=False, server_default="0"), sa.Column("max_attempts", sa.Integer(), nullable=False, server_default="5"), sa.Column("started_at", sa.DateTime(timezone=True), nullable=True), sa.Column("finished_at", sa.DateTime(timezone=True), nullable=True), sa.Column("error", sa.Text(), nullable=True), sa.Column("result_aurora_id", sa.Uuid(), nullable=True), sa.UniqueConstraint("idempotency_key", name="uq_ingestion_job_idempotency"), ) op.create_table( "provider_records", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column( "provider_id", sa.Uuid(), sa.ForeignKey("providers.id", ondelete="CASCADE"), nullable=False, ), sa.Column("entity_type", sa.String(30), nullable=False), sa.Column("external_id", sa.String(200), nullable=False), sa.Column("aurora_entity_id", sa.Uuid(), nullable=True), sa.Column( "raw_data", postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default="{}" ), sa.Column("source_version", sa.String(100), nullable=True), sa.Column("match_confidence", MATCH_CONFIDENCE, nullable=False, server_default="new"), sa.Column("match_notes", sa.Text(), nullable=True), sa.Column( "ingestion_job_id", sa.Uuid(), sa.ForeignKey("ingestion_jobs.id", ondelete="SET NULL"), nullable=True, ), sa.Column( "last_synced_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False, ), sa.UniqueConstraint( "provider_id", "entity_type", "external_id", name="uq_provider_record_source" ), ) # --- Genres / keywords ---------------------------------------------- op.create_table( "genres", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column("name", sa.String(100), nullable=False, unique=True), sa.Column("slug", sa.String(100), nullable=False, unique=True), ) op.create_table( "keywords", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column("name", sa.String(150), nullable=False, unique=True), sa.Column("slug", sa.String(150), nullable=False, unique=True), ) # --- Companies -------------------------------------------------------- op.create_table( "companies", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("name", sa.String(300), nullable=False), sa.Column( "company_type", COMPANY_TYPE, nullable=False, server_default="production_company" ), sa.Column("description", sa.Text(), nullable=True), sa.Column("country_code", sa.String(2), sa.ForeignKey("countries.code"), nullable=True), sa.Column("headquarters", sa.String(300), nullable=True), sa.Column("homepage", sa.String(500), nullable=True), sa.Column( "parent_company_id", sa.Uuid(), sa.ForeignKey("companies.id", ondelete="SET NULL"), nullable=True, ), ) op.create_index("ix_companies_name", "companies", ["name"]) # --- Collections -------------------------------------------------------- op.create_table( "collections", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("name", sa.String(300), nullable=False), sa.Column("overview", sa.Text(), nullable=True), sa.Column("slug", sa.String(350), nullable=False, unique=True), ) op.create_index("ix_collections_name", "collections", ["name"]) # --- People ----------------------------------------------------------- op.create_table( "people", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("name", sa.String(300), nullable=False), sa.Column( "aliases", postgresql.JSONB(astext_type=sa.Text()), nullable=False, server_default="[]" ), sa.Column("biography", sa.Text(), nullable=True), sa.Column("birthday", sa.Date(), nullable=True), sa.Column("deathday", sa.Date(), nullable=True), sa.Column("birthplace", sa.String(300), nullable=True), sa.Column("gender", sa.String(30), nullable=True), sa.Column("known_for_department", sa.String(100), nullable=True), sa.Column("popularity", sa.Float(), nullable=True), ) op.create_index("ix_people_name", "people", ["name"]) # --- Movies ------------------------------------------------------------- op.create_table( "movies", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("title", sa.String(500), nullable=False), sa.Column("original_title", sa.String(500), nullable=True), sa.Column("sort_title", sa.String(500), nullable=False), sa.Column("overview", sa.Text(), nullable=True), sa.Column("tagline", sa.Text(), nullable=True), sa.Column("status", MOVIE_STATUS, nullable=False, server_default="released"), sa.Column( "original_language_code", sa.String(10), sa.ForeignKey("languages.code"), nullable=True ), sa.Column("primary_release_date", sa.Date(), nullable=True), sa.Column("runtime_minutes", sa.Integer(), nullable=True), sa.Column("budget", sa.BigInteger(), nullable=True), sa.Column("revenue", sa.BigInteger(), nullable=True), sa.Column("popularity", sa.Float(), nullable=True), sa.Column("vote_average", sa.Float(), nullable=True), sa.Column("vote_count", sa.Integer(), nullable=True), sa.Column("adult", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column( "collection_id", sa.Uuid(), sa.ForeignKey("collections.id", ondelete="SET NULL"), nullable=True, ), sa.Column( "source_provider_id", sa.Uuid(), sa.ForeignKey("providers.id", ondelete="SET NULL"), nullable=True, ), ) op.create_index("ix_movies_title", "movies", ["title"]) op.create_index("ix_movies_sort_title", "movies", ["sort_title"]) op.create_index("ix_movies_primary_release_date", "movies", ["primary_release_date"]) op.create_table( "movie_alternate_titles", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column( "movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), nullable=False ), sa.Column("title", sa.String(500), nullable=False), sa.Column("country_code", sa.String(2), sa.ForeignKey("countries.code"), nullable=True), sa.Column("type", sa.String(50), nullable=True), ) op.create_index( "ix_movie_alternate_titles_movie_id", "movie_alternate_titles", ["movie_id"] ) op.create_table( "movie_spoken_languages", sa.Column( "movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True, ), sa.Column( "language_code", sa.String(10), sa.ForeignKey("languages.code"), primary_key=True, ), ) op.create_table( "movie_production_countries", sa.Column( "movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True, ), sa.Column( "country_code", sa.String(2), sa.ForeignKey("countries.code"), primary_key=True, ), ) op.create_table( "release_dates", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column("entity_type", sa.String(30), nullable=False, server_default="movie"), sa.Column("entity_id", sa.Uuid(), nullable=False), sa.Column("country_code", sa.String(2), sa.ForeignKey("countries.code"), nullable=False), sa.Column("release_date", sa.Date(), nullable=False), sa.Column("release_type", sa.String(50), nullable=True), sa.Column( "certification_id", sa.Uuid(), sa.ForeignKey("certifications.id", ondelete="SET NULL"), nullable=True, ), sa.Column("note", sa.Text(), nullable=True), ) op.create_index("ix_release_dates_entity_id", "release_dates", ["entity_id"]) op.create_table( "movie_recommendations", sa.Column( "movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True, ), sa.Column( "recommended_movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True, ), sa.Column("rank", sa.Integer(), nullable=True), sa.Column("source", sa.String(50), nullable=True), ) op.create_table( "movie_genres", sa.Column( "movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True ), sa.Column( "genre_id", sa.Uuid(), sa.ForeignKey("genres.id", ondelete="CASCADE"), primary_key=True ), ) op.create_table( "movie_keywords", sa.Column( "movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True ), sa.Column( "keyword_id", sa.Uuid(), sa.ForeignKey("keywords.id", ondelete="CASCADE"), primary_key=True, ), ) op.create_table( "movie_companies", sa.Column( "movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True ), sa.Column( "company_id", sa.Uuid(), sa.ForeignKey("companies.id", ondelete="CASCADE"), primary_key=True, ), sa.Column("role", sa.String(50), nullable=True), ) # --- Series / Seasons / Episodes --------------------------------------- op.create_table( "series", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("title", sa.String(500), nullable=False), sa.Column("original_title", sa.String(500), nullable=True), sa.Column("sort_title", sa.String(500), nullable=False), sa.Column("overview", sa.Text(), nullable=True), sa.Column("tagline", sa.Text(), nullable=True), sa.Column("status", SERIES_STATUS, nullable=False, server_default="returning_series"), sa.Column( "original_language_code", sa.String(10), sa.ForeignKey("languages.code"), nullable=True ), sa.Column("first_air_date", sa.Date(), nullable=True), sa.Column("last_air_date", sa.Date(), nullable=True), sa.Column("number_of_seasons", sa.Integer(), nullable=True), sa.Column("number_of_episodes", sa.Integer(), nullable=True), sa.Column("popularity", sa.Float(), nullable=True), sa.Column("vote_average", sa.Float(), nullable=True), sa.Column("vote_count", sa.Integer(), nullable=True), sa.Column( "source_provider_id", sa.Uuid(), sa.ForeignKey("providers.id", ondelete="SET NULL"), nullable=True, ), ) op.create_index("ix_series_title", "series", ["title"]) op.create_index("ix_series_sort_title", "series", ["sort_title"]) op.create_index("ix_series_first_air_date", "series", ["first_air_date"]) op.create_table( "series_alternate_titles", sa.Column("id", sa.Uuid(), primary_key=True), sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), nullable=False ), sa.Column("title", sa.String(500), nullable=False), sa.Column("country_code", sa.String(2), sa.ForeignKey("countries.code"), nullable=True), sa.Column("type", sa.String(50), nullable=True), ) op.create_index( "ix_series_alternate_titles_series_id", "series_alternate_titles", ["series_id"] ) op.create_table( "series_spoken_languages", sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), primary_key=True, ), sa.Column( "language_code", sa.String(10), sa.ForeignKey("languages.code"), primary_key=True, ), ) op.create_table( "series_production_countries", sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), primary_key=True, ), sa.Column( "country_code", sa.String(2), sa.ForeignKey("countries.code"), primary_key=True, ), ) op.create_table( "seasons", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), nullable=False ), sa.Column("season_number", sa.Integer(), nullable=False), sa.Column("title", sa.String(500), nullable=True), sa.Column("overview", sa.Text(), nullable=True), sa.Column("air_date", sa.Date(), nullable=True), sa.Column("episode_count", sa.Integer(), nullable=True), sa.UniqueConstraint("series_id", "season_number", name="uq_season_number"), ) op.create_index("ix_seasons_series_id", "seasons", ["series_id"]) op.create_table( "episodes", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column( "season_id", sa.Uuid(), sa.ForeignKey("seasons.id", ondelete="CASCADE"), nullable=False ), sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), nullable=False ), sa.Column("season_number", sa.Integer(), nullable=False), sa.Column("episode_number", sa.Integer(), nullable=False), sa.Column("title", sa.String(500), nullable=True), sa.Column("overview", sa.Text(), nullable=True), sa.Column("air_date", sa.Date(), nullable=True), sa.Column("runtime_minutes", sa.Integer(), nullable=True), sa.UniqueConstraint("season_id", "episode_number", name="uq_episode_number"), ) op.create_index("ix_episodes_season_id", "episodes", ["season_id"]) op.create_index("ix_episodes_series_id", "episodes", ["series_id"]) op.create_table( "series_genres", sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), primary_key=True ), sa.Column( "genre_id", sa.Uuid(), sa.ForeignKey("genres.id", ondelete="CASCADE"), primary_key=True ), ) op.create_table( "series_keywords", sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), primary_key=True ), sa.Column( "keyword_id", sa.Uuid(), sa.ForeignKey("keywords.id", ondelete="CASCADE"), primary_key=True, ), ) op.create_table( "series_companies", sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), primary_key=True ), sa.Column( "company_id", sa.Uuid(), sa.ForeignKey("companies.id", ondelete="CASCADE"), primary_key=True, ), sa.Column("role", sa.String(50), nullable=True), ) # --- Collection memberships --------------------------------------------- op.create_table( "collection_movies", sa.Column( "collection_id", sa.Uuid(), sa.ForeignKey("collections.id", ondelete="CASCADE"), primary_key=True, ), sa.Column( "movie_id", sa.Uuid(), sa.ForeignKey("movies.id", ondelete="CASCADE"), primary_key=True ), sa.Column("sort_order", sa.Integer(), nullable=True), ) op.create_table( "collection_series", sa.Column( "collection_id", sa.Uuid(), sa.ForeignKey("collections.id", ondelete="CASCADE"), primary_key=True, ), sa.Column( "series_id", sa.Uuid(), sa.ForeignKey("series.id", ondelete="CASCADE"), primary_key=True ), sa.Column("sort_order", sa.Integer(), nullable=True), ) op.create_table( "collection_relations", sa.Column( "collection_id", sa.Uuid(), sa.ForeignKey("collections.id", ondelete="CASCADE"), primary_key=True, ), sa.Column( "related_collection_id", sa.Uuid(), sa.ForeignKey("collections.id", ondelete="CASCADE"), primary_key=True, ), sa.Column("relation_type", sa.String(50), nullable=False, server_default="related"), ) # --- Polymorphic satellite tables --------------------------------------- op.create_table( "credits", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column( "person_id", sa.Uuid(), sa.ForeignKey("people.id", ondelete="CASCADE"), nullable=False ), sa.Column("entity_type", sa.String(30), nullable=False), sa.Column("entity_id", sa.Uuid(), nullable=False), sa.Column("department", sa.String(100), nullable=False), sa.Column("job", sa.String(150), nullable=True), sa.Column("character", sa.String(300), nullable=True), sa.Column("billing_order", sa.Integer(), nullable=True), sa.Column("is_guest", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("episode_count", sa.Integer(), nullable=True), sa.Column( "provider_id", sa.Uuid(), sa.ForeignKey("providers.id", ondelete="SET NULL"), nullable=True, ), ) op.create_index("ix_credits_entity", "credits", ["entity_type", "entity_id"]) op.create_index("ix_credits_person_id", "credits", ["person_id"]) op.create_table( "artwork", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("entity_type", sa.String(30), nullable=False), sa.Column("entity_id", sa.Uuid(), nullable=False), sa.Column("artwork_type", ARTWORK_TYPE, nullable=False), sa.Column("language_code", sa.String(10), sa.ForeignKey("languages.code"), nullable=True), sa.Column("width", sa.Integer(), nullable=True), sa.Column("height", sa.Integer(), nullable=True), sa.Column("aspect_ratio", sa.Float(), nullable=True), sa.Column("vote_average", sa.Float(), nullable=True), sa.Column("is_primary", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column( "provider_id", sa.Uuid(), sa.ForeignKey("providers.id", ondelete="CASCADE"), nullable=False, ), sa.Column("provider_asset_id", sa.String(200), nullable=True), sa.Column("original_url", sa.String(1000), nullable=False), sa.Column("local_path", sa.String(1000), nullable=True), sa.Column("checksum", sa.String(128), nullable=True), sa.Column("attribution", sa.Text(), nullable=True), sa.Column("license", sa.Text(), nullable=True), ) op.create_index( "ix_artwork_entity", "artwork", ["entity_type", "entity_id", "artwork_type"] ) op.create_table( "videos", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("entity_type", sa.String(30), nullable=False), sa.Column("entity_id", sa.Uuid(), nullable=False), sa.Column("video_type", VIDEO_TYPE, nullable=False), sa.Column( "provider_id", sa.Uuid(), sa.ForeignKey("providers.id", ondelete="CASCADE"), nullable=False, ), sa.Column("provider_video_id", sa.String(200), nullable=False), sa.Column("site", sa.String(100), nullable=True), sa.Column("url", sa.String(1000), nullable=False), sa.Column("language_code", sa.String(10), sa.ForeignKey("languages.code"), nullable=True), sa.Column("country_code", sa.String(2), sa.ForeignKey("countries.code"), nullable=True), sa.Column("is_official", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("published_at", sa.Date(), nullable=True), sa.UniqueConstraint( "provider_id", "provider_video_id", name="uq_video_provider_source" ), ) op.create_index("ix_videos_entity", "videos", ["entity_type", "entity_id"]) op.create_table( "external_ids", sa.Column("id", sa.Uuid(), primary_key=True), *_timestamps(), sa.Column("entity_type", sa.String(30), nullable=False), sa.Column("entity_id", sa.Uuid(), nullable=False), sa.Column( "provider_id", sa.Uuid(), sa.ForeignKey("providers.id", ondelete="CASCADE"), nullable=False, ), sa.Column("external_id", sa.String(200), nullable=False), sa.Column("url", sa.String(500), nullable=True), sa.UniqueConstraint( "provider_id", "entity_type", "external_id", name="uq_external_id_source" ), sa.UniqueConstraint( "entity_type", "entity_id", "provider_id", name="uq_external_id_entity_provider", ), ) # --- Search indexes (section 16) ---------------------------------------- op.execute( "CREATE INDEX ix_movies_title_trgm ON movies USING gin (title gin_trgm_ops)" ) op.execute( "CREATE INDEX ix_movies_title_fts ON movies " "USING gin (to_tsvector('simple', coalesce(title, '') || ' ' || coalesce(original_title, '')))" ) op.execute( "CREATE INDEX ix_movie_alt_titles_trgm ON movie_alternate_titles " "USING gin (title gin_trgm_ops)" ) op.execute( "CREATE INDEX ix_series_title_trgm ON series USING gin (title gin_trgm_ops)" ) op.execute( "CREATE INDEX ix_series_title_fts ON series " "USING gin (to_tsvector('simple', coalesce(title, '') || ' ' || coalesce(original_title, '')))" ) op.execute( "CREATE INDEX ix_series_alt_titles_trgm ON series_alternate_titles " "USING gin (title gin_trgm_ops)" ) op.execute("CREATE INDEX ix_people_name_trgm ON people USING gin (name gin_trgm_ops)") op.execute( "CREATE INDEX ix_people_name_fts ON people USING gin (to_tsvector('simple', name))" ) op.execute( "CREATE INDEX ix_collections_name_trgm ON collections USING gin (name gin_trgm_ops)" ) op.execute( "CREATE INDEX ix_companies_name_trgm ON companies USING gin (name gin_trgm_ops)" ) def downgrade() -> None: op.drop_table("external_ids") op.drop_table("videos") op.drop_table("artwork") op.drop_table("credits") op.drop_table("collection_relations") op.drop_table("collection_series") op.drop_table("collection_movies") op.drop_table("series_companies") op.drop_table("series_keywords") op.drop_table("series_genres") op.drop_table("episodes") op.drop_table("seasons") op.drop_table("series_production_countries") op.drop_table("series_spoken_languages") op.drop_table("series_alternate_titles") op.drop_table("series") op.drop_table("movie_companies") op.drop_table("movie_keywords") op.drop_table("movie_genres") op.drop_table("movie_recommendations") op.drop_table("release_dates") op.drop_table("movie_production_countries") op.drop_table("movie_spoken_languages") op.drop_table("movie_alternate_titles") op.drop_table("movies") op.drop_table("people") op.drop_table("collections") op.drop_table("companies") op.drop_table("keywords") op.drop_table("genres") op.drop_table("provider_records") op.drop_table("ingestion_jobs") op.drop_table("providers") op.drop_table("certifications") op.drop_table("countries") op.drop_table("languages") bind = op.get_bind() for enum_type in ( MATCH_CONFIDENCE, INGESTION_JOB_STATUS, COMPANY_TYPE, VIDEO_TYPE, ARTWORK_TYPE, SERIES_STATUS, MOVIE_STATUS, ): enum_type.drop(bind, checkfirst=True) op.execute("DROP EXTENSION IF EXISTS pg_trgm")