diff --git a/apply-supplementary-indexes-migration.sql b/apply-supplementary-indexes-migration.sql new file mode 100644 index 00000000..6f531046 --- /dev/null +++ b/apply-supplementary-indexes-migration.sql @@ -0,0 +1,121 @@ +-- ================================================ +-- Jellyfin PostgreSQL Migration +-- Migration: 20260227000000_AddSupplementaryIndexes +-- Purpose: Add performance indexes for improved query performance +-- ================================================ +-- +-- INSTRUCTIONS: +-- 1. Connect to your Jellyfin PostgreSQL database +-- 2. Run this script using psql, pgAdmin, or any PostgreSQL client +-- 3. Verify indexes are created (see verification query at end) +-- +-- ================================================ + +BEGIN; + +-- Check if migration was already applied +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 FROM library."__EFMigrationsHistory" + WHERE "MigrationId" = '20260227000000_AddSupplementaryIndexes' + ) THEN + RAISE NOTICE 'Migration 20260227000000_AddSupplementaryIndexes already applied. Skipping...'; + ELSE + RAISE NOTICE 'Applying migration 20260227000000_AddSupplementaryIndexes...'; + END IF; +END $$; + +COMMIT; + +-- ================================================ +-- Create Indexes (CONCURRENTLY - safe for production) +-- ================================================ + +-- 1. Composite index for filtered library browsing (type + virtual + parent) +-- Use case: Browse items by type in a specific library, excluding virtual items +-- Improves queries like: "SELECT * FROM BaseItems WHERE Type='Movie' AND IsVirtualItem=false AND TopParentId='xyz'" +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_type_isvirtualitem_topparentid +ON library."BaseItems" ("Type", "IsVirtualItem", "TopParentId") +WHERE "IsVirtualItem" = false; + +-- 2. Index for folder hierarchy queries +-- Use case: Navigate folder structures efficiently +-- Improves queries like: "SELECT * FROM BaseItems WHERE TopParentId='xyz' AND IsFolder=true" +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_topparentid_isfolder +ON library."BaseItems" ("TopParentId", "IsFolder", "IsVirtualItem") +WHERE "TopParentId" IS NOT NULL; + +-- 3. Index for recently added content with filters +-- Use case: "Recently Added" view across all libraries +-- Improves queries like: "SELECT * FROM BaseItems WHERE IsVirtualItem=false ORDER BY DateCreated DESC" +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_datecreated_filtered +ON library."BaseItems" ("DateCreated" DESC, "Type", "IsVirtualItem") +WHERE "DateCreated" IS NOT NULL AND "IsVirtualItem" = false; + +-- 4. Index for reverse lookup (find items by genre/tag) +-- Use case: "Show all items with genre 'Action'" +-- Improves queries like: "SELECT ItemId FROM ItemValuesMap WHERE ItemValueId='action-genre-id'" +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvaluesmap_itemvalueid_itemid +ON library."ItemValuesMap" ("ItemValueId", "ItemId"); + +-- 5. Index for user-specific activity queries +-- Use case: "Show activity for user X" +-- Improves queries like: "SELECT * FROM ActivityLogs WHERE UserId='user-id' ORDER BY DateCreated DESC" +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 FROM information_schema.tables + WHERE table_schema = 'activitylog' + AND table_name = 'ActivityLogs' + ) THEN + RAISE NOTICE 'Creating activity log index...'; + CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_activitylogs_userid_datecreated + ON activitylog."ActivityLogs" ("UserId", "DateCreated" DESC) + WHERE "UserId" IS NOT NULL; + ELSE + RAISE NOTICE 'ActivityLogs table does not exist. Skipping activity log index.'; + END IF; +END $$; + +-- 6. Index for episode deduplication by PresentationUniqueKey +-- Use case: Group and deduplicate episodes (used in GetItems queries) +-- Improves queries like: "SELECT * FROM BaseItems WHERE Type='Episode' AND PresentationUniqueKey='xyz'" +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_presentationuniquekey_episodes +ON library."BaseItems" ("PresentationUniqueKey", "TopParentId", "IsVirtualItem") +WHERE "Type" = 'MediaBrowser.Controller.Entities.TV.Episode' AND "PresentationUniqueKey" IS NOT NULL; + +-- ================================================ +-- Update migration history +-- ================================================ + +BEGIN; + +INSERT INTO library."__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20260227000000_AddSupplementaryIndexes', '11.0.0') +ON CONFLICT ("MigrationId") DO NOTHING; + +COMMIT; + +-- ================================================ +-- Verification Query +-- ================================================ +-- Run this to verify all indexes were created successfully: + +SELECT + schemaname AS "Schema", + tablename AS "Table", + indexname AS "Index Name", + indexdef AS "Definition" +FROM pg_indexes +WHERE indexname IN ( + 'idx_baseitems_type_isvirtualitem_topparentid', + 'idx_baseitems_topparentid_isfolder', + 'idx_baseitems_datecreated_filtered', + 'idx_itemvaluesmap_itemvalueid_itemid', + 'idx_activitylogs_userid_datecreated', + 'idx_baseitems_presentationuniquekey_episodes' +) +ORDER BY schemaname, tablename, indexname; + +-- Expected: 6 rows (or 5 if ActivityLogs table doesn't exist)