feat: add performance indexes for improved query efficiency

This commit is contained in:
Rodrigo Verdiani 2026-04-15 11:18:50 -03:00
parent 1a41ae2a8d
commit da3114b85e

View File

@ -0,0 +1,98 @@
-- ============================================================
-- V0012 — Add performance indexes
-- ============================================================
-- mangas
-- follow: findByFollowTrue() hot path for the scheduled follow-update task
CREATE INDEX IF NOT EXISTS idx_mangas_follow ON mangas (follow);
-- state: natural filter (PENDING, ACTIVE, etc.)
CREATE INDEX IF NOT EXISTS idx_mangas_state ON mangas (state);
-- adult: MangaSpecification always filters adult = false by default
CREATE INDEX IF NOT EXISTS idx_mangas_adult ON mangas (adult);
-- score: MangaSpecification filters score >= ?
CREATE INDEX IF NOT EXISTS idx_mangas_score ON mangas (score);
-- status: plain index for exact-match; functional index for LOWER(status) IN (...)
CREATE INDEX IF NOT EXISTS idx_mangas_status ON mangas (status);
CREATE INDEX IF NOT EXISTS idx_mangas_status_lower ON mangas (LOWER(status));
-- title: findByTitleIgnoreCase + LIKE search via LOWER(title)
CREATE INDEX IF NOT EXISTS idx_mangas_title_lower ON mangas (LOWER(title));
-- ============================================================
-- images
-- object_key: needed for the page-by-page IN-query in ImageCleanupTask
CREATE INDEX IF NOT EXISTS idx_images_object_key ON images (object_key);
-- ============================================================
-- manga_contents
-- manga_content_provider_id: FK join for every chapter lookup
CREATE INDEX IF NOT EXISTS idx_manga_contents_provider_id ON manga_contents (manga_content_provider_id);
-- (manga_content_provider_id, LOWER(url)): covers existsByMangaContentProvider_IdAndUrlIgnoreCase
CREATE INDEX IF NOT EXISTS idx_manga_contents_provider_url ON manga_contents (manga_content_provider_id, LOWER(url));
-- downloaded: ingestion pipelines filter on downloaded = false
CREATE INDEX IF NOT EXISTS idx_manga_contents_downloaded ON manga_contents (downloaded);
-- ============================================================
-- manga_content_images
-- manga_content_id: findAllByMangaContent + existsByMangaContent_IdAndPosition
CREATE INDEX IF NOT EXISTS idx_manga_content_images_content_id ON manga_content_images (manga_content_id);
-- (manga_content_id, position): covers the position-existence check and ordered reads
CREATE INDEX IF NOT EXISTS idx_manga_content_images_content_position ON manga_content_images (manga_content_id, position);
-- image_id: FK into the 500k-row images table — cascade deletes and joins
CREATE INDEX IF NOT EXISTS idx_manga_content_images_image_id ON manga_content_images (image_id);
-- ============================================================
-- manga_content_provider
-- manga_id: findByManga_IdAndContentProvider_Id + @OneToMany collection load
CREATE INDEX IF NOT EXISTS idx_manga_content_provider_manga_id ON manga_content_provider (manga_id);
-- LOWER(manga_title): existsByMangaTitleIgnoreCaseAndContentProvider_Id
CREATE INDEX IF NOT EXISTS idx_manga_content_provider_title_lower ON manga_content_provider (LOWER(manga_title));
-- ============================================================
-- manga_ingest_reviews
-- content_provider_id: FK lookups and cascade deletes
CREATE INDEX IF NOT EXISTS idx_manga_ingest_reviews_provider_id ON manga_ingest_reviews (content_provider_id);
-- ============================================================
-- user_manga_content_read
-- user_id alone: countByUser, countDistinctMangaByUser
-- (The UNIQUE constraint on (user_id, manga_content_id) exists but a single-column index
-- on user_id allows efficient count/scan queries without reading the full composite key.)
CREATE INDEX IF NOT EXISTS idx_user_manga_content_read_user_id ON user_manga_content_read (user_id);
-- (user_id, created_at DESC): eliminates the sort step in findTop10ByUserOrderByCreatedAtDesc
CREATE INDEX IF NOT EXISTS idx_user_manga_content_read_user_created ON user_manga_content_read (user_id, created_at DESC);
-- ============================================================
-- user_manga_follow
-- user_id: findByUser, existsByUserAndManga
CREATE INDEX IF NOT EXISTS idx_user_manga_follow_user_id ON user_manga_follow (user_id);
-- manga_id: findByManga, existsByManga
CREATE INDEX IF NOT EXISTS idx_user_manga_follow_manga_id ON user_manga_follow (manga_id);
-- Composite unique: existsByUserAndManga / findByUserAndManga point lookups
-- (no constraint existed on this table before)
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_manga_follow_user_manga ON user_manga_follow (user_id, manga_id);
-- ============================================================
-- manga_import_job
-- status: MangaImportJobSpecification filters on status
CREATE INDEX IF NOT EXISTS idx_manga_import_job_status ON manga_import_job (status);