perf(database): partial covering index for visible categories

Three queries filter on categories.hidden = false:

- getGlobalRecentlyAdded — every dashboard load (now sorts by added DESC
  on the indexed content side after the prior CAST removal)
- searchContent — Xtream search with excludeHidden
- globalSearch — global Xtream search with excludeHidden

All three start from content and join into categories on the
category_id PK. The hidden filter is then applied per-row, requiring a
row lookup for every result candidate — even before LIMIT.

Add a partial covering index on categories: idx_categories_visible(id,
playlist_id, type) WHERE hidden = 0. Two wins:

- Covering: SQLite serves the join (id) plus filters (playlist_id,
  type) and the hidden = 0 predicate directly from the index without
  ever touching the categories row.
- Partial: hidden categories are absent from the index, so they're
  pruned before any work happens — no row lookup, no comparison.

Schema mirrored in Drizzle so the ORM stays the single source of
truth. CREATE INDEX IF NOT EXISTS so existing DBs pick it up on next
startup with no migration needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
This commit is contained in:
4grayandClaude Opus 4.7 committed 2026-05-02 10:27:38 +02:00
1 parent 13acbe7e3d
commit 29fe8b905c
2 files changed
+13

No files matched your search

@@ -147,6 +147,13 @@ const CREATE_TABLE_STATEMENTS = [
`CREATE INDEX IF NOT EXISTS idx_content_xtream ON content(xtream_id)`,
`CREATE INDEX IF NOT EXISTS idx_content_type_added ON content(type, added)`,
`CREATE INDEX IF NOT EXISTS idx_categories_type ON categories(type)`,
// Partial covering index for visible categories — supports the dashboard's
// getGlobalRecentlyAdded plus searchContent/globalSearch when excludeHidden
// is set. SQLite can satisfy the join (category_id PK lookup) plus the
// hidden = 0 filter directly from this index without touching the
// categories row, and hidden categories are absent so they're skipped
// before any row lookup.
`CREATE INDEX IF NOT EXISTS idx_categories_visible ON categories(id, playlist_id, type) WHERE hidden = 0`,
`CREATE UNIQUE INDEX IF NOT EXISTS favorites_content_playlist_unique ON favorites(content_id, playlist_id)`,
`CREATE INDEX IF NOT EXISTS favorites_playlist_idx ON favorites(playlist_id)`,
`CREATE INDEX IF NOT EXISTS favorites_content_idx ON favorites(content_id)`,
+6
View File
@@ -72,6 +72,12 @@ export const categories = sqliteTable(
playlistTypeXtreamUnique: uniqueIndex(
'categories_playlist_type_xtream_unique'
).on(table.playlistId, table.type, table.xtreamId),
// Partial covering index for visible categories — see connection.ts
// for the full rationale. SQLite can satisfy joins + hidden filter
// directly from this index for the dashboard / search queries.
visibleIdx: index('idx_categories_visible')
.on(table.id, table.playlistId, table.type)
.where(sql`hidden = 0`),
})
);