From 9ecf4d44d3545fc19330d11245883be6d0884869 Mon Sep 17 00:00:00 2001 From: 4gray Date: Fri, 1 May 2026 21:11:44 +0200 Subject: [PATCH] perf(database): add composite indexes for hot ORDER BY paths The existing single-column indexes force a sort step for the most common list queries. These three composite indexes let SQLite scan in index order and skip the sort entirely: - recently_viewed(playlist_id, viewed_at DESC): getRecentItems(): WHERE playlist_id = ? ORDER BY viewed_at DESC LIMIT 100 - favorites(playlist_id, position, added_at DESC): getFavorites/getGlobalFavorites/getAllGlobalFavorites(): WHERE playlist_id = ? ORDER BY position ASC, added_at DESC - playback_positions(playlist_id, updated_at DESC): getRecentPlaybackPositions(): WHERE playlist_id = ? ORDER BY updated_at DESC LIMIT 20 CREATE INDEX IF NOT EXISTS so existing DBs pick up the new indexes on next startup with no migration required. Mirrored in the Drizzle schema using sql\`col DESC\` so the ORM stays the source of truth. Co-Authored-By: Claude Opus 4.7 (1M context) Entire-Checkpoint: 3d6901072049 --- libs/shared/database/src/lib/connection.ts | 3 +++ libs/shared/database/src/lib/schema.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/libs/shared/database/src/lib/connection.ts b/libs/shared/database/src/lib/connection.ts index e3fc06d4d..e9a820f94 100644 --- a/libs/shared/database/src/lib/connection.ts +++ b/libs/shared/database/src/lib/connection.ts @@ -150,6 +150,8 @@ const CREATE_TABLE_STATEMENTS = [ `CREATE UNIQUE INDEX IF NOT EXISTS recently_viewed_content_playlist_unique ON recently_viewed(content_id, playlist_id)`, `CREATE INDEX IF NOT EXISTS recently_viewed_playlist_idx ON recently_viewed(playlist_id)`, `CREATE INDEX IF NOT EXISTS recently_viewed_viewed_at_idx ON recently_viewed(viewed_at)`, + `CREATE INDEX IF NOT EXISTS recently_viewed_playlist_viewed_idx ON recently_viewed(playlist_id, viewed_at DESC)`, + `CREATE INDEX IF NOT EXISTS favorites_playlist_position_idx ON favorites(playlist_id, position, added_at DESC)`, // EPG tables `CREATE TABLE IF NOT EXISTS epg_channels ( id TEXT PRIMARY KEY, @@ -220,6 +222,7 @@ const CREATE_TABLE_STATEMENTS = [ `CREATE INDEX IF NOT EXISTS playback_positions_playlist_idx ON playback_positions(playlist_id)`, `CREATE INDEX IF NOT EXISTS playback_positions_series_idx ON playback_positions(series_xtream_id)`, `CREATE INDEX IF NOT EXISTS playback_positions_updated_idx ON playback_positions(updated_at)`, + `CREATE INDEX IF NOT EXISTS playback_positions_playlist_updated_idx ON playback_positions(playlist_id, updated_at DESC)`, // Downloads table `CREATE TABLE IF NOT EXISTS downloads ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/libs/shared/database/src/lib/schema.ts b/libs/shared/database/src/lib/schema.ts index d34a53ef8..aa9b2a3e6 100644 --- a/libs/shared/database/src/lib/schema.ts +++ b/libs/shared/database/src/lib/schema.ts @@ -129,6 +129,10 @@ export const recentlyViewed = sqliteTable( ).on(table.contentId, table.playlistId), playlistIdx: index('recently_viewed_playlist_idx').on(table.playlistId), viewedAtIdx: index('recently_viewed_viewed_at_idx').on(table.viewedAt), + playlistViewedIdx: index('recently_viewed_playlist_viewed_idx').on( + table.playlistId, + sql`${table.viewedAt} DESC` + ), }) ); @@ -153,6 +157,11 @@ export const favorites = sqliteTable( ).on(table.contentId, table.playlistId), playlistIdx: index('favorites_playlist_idx').on(table.playlistId), contentIdx: index('favorites_content_idx').on(table.contentId), + playlistPositionIdx: index('favorites_playlist_position_idx').on( + table.playlistId, + table.position, + sql`${table.addedAt} DESC` + ), }) ); @@ -241,6 +250,9 @@ export const playbackPositions = sqliteTable( table.seriesXtreamId ), updatedIdx: index('playback_positions_updated_idx').on(table.updatedAt), + playlistUpdatedIdx: index( + 'playback_positions_playlist_updated_idx' + ).on(table.playlistId, sql`${table.updatedAt} DESC`), }) );