navidrome/ui/src
Deluan Quintão d4387c5502
perf(db): index media_file album/artist sort orders (#5706)
* perf(db): add composite indexes for song list album/artist sorts

The media_file sort mappings for album, artist and albumArtist expand to
multi-column ORDER BY clauses that no existing index could satisfy, so SQLite
fell back to a full table scan plus a temp B-tree sort of every row (including
the large lyrics/tags/full_text columns) even for a single 15-item page. On a
96K-track library this made /api/song?_sort=album take 3.6s on a cold cache.

Add composite indexes matching the three sort mappings, allowing the query to
walk the index and stop at the page size, in both directions. Drop the now
redundant single-column order_album_name/order_artist_name indexes (strict
prefixes of the new composites) and three indexes with no query path:
birth_time is only read in Go code, and artist/album_artist text column
lookups go through the media_file_artists table instead.

* fix(ui): make composer and track number columns non-sortable in song list

Clicking the Composer header was a silent no-op: composer is not a media_file
column, so the native API's sanitizeSort drops the sort and returns rows in
table order. Track number sorting across the whole library is not meaningful
and cannot use an index (the existing index leads with disc_number). Mark both
columns sortable={false}, like quality and mood.

* test(persistence): add sort index coverage test for large tables

Guard against sort options silently losing index support: every sort mapping
on media_file, album and artist is now verified with EXPLAIN QUERY PLAN to be
satisfiable by an index (both directions), so adding a mapping or dropping an
index that reintroduces a full-table temp B-tree sort fails the test. Sorts
that genuinely cannot use an index (random, annotation-join columns, JSON
expressions) must be declared in an exceptions list with the reason, keeping
the trade-off visible in review.

To make the sort mappings the complete declared sort surface, add identity
mappings for the media_file columns the UI sorts by without a mapping (year,
genre, duration, channels, bpm, path, comment, play_count, play_date, rating).
These are behaviorally no-ops: the same ORDER BY was previously produced by
the field whitelist fallback.

* perf(db): drop PreferSortTags expression indexes from media_file

The media_file sort_title/sort_artist_name/sort_album_name expression indexes
are only usable when PreferSortTags is enabled - a config reported by ~0.1% of
installations (insights, week of 2026-06-22) - yet every install pays their
storage (~8.6MB on a 96K-track library) and scanner write overhead. Drop them:
PreferSortTags installs fall back to a full sort for title/artist/album orders,
everyone else gets smaller DBs and cheaper writes. The order_album_name and
order_artist_name collation checks remain valid, now satisfied by the composite
sort indexes.

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
2026-07-03 08:58:55 -04:00
..
actions feat(ui): replace UI scrobble with reportPlayback and redesign NowPlaying panel (#5448) 2026-05-01 15:27:32 -04:00
album fix(ui): show album tile actions on keyboard focus (#5434) 2026-04-28 22:48:59 -04:00
artist feat(ui): add Not Starred filter option (#5362) 2026-04-27 18:21:32 -04:00
audioplayer fix(ui): report playback when restarting current track via prev 2026-05-22 22:23:36 -03:00
common fix(ui): refine image filters for playing and paused states in SquiddiesGlass 2026-04-08 13:13:56 -04:00
dataProvider fix(ui): make self-service profile edits report their outcome (#5699) 2026-07-01 21:31:29 -04:00
dialogs feat(ui): add download config toml link, disable copy when clipboard not available (#5035) 2026-02-12 10:54:04 -05:00
i18n fix(transcoding): honor player forced format on the WebUI transcode flow (#5613) 2026-06-14 20:52:19 -04:00
icons build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
layout fix(ui): load ND_DEFAULTLANGUAGE on app startup (#4000) 2026-06-09 19:27:15 -04:00
library feat(ui): conditionally display 'path' field in LibraryList for desktop view 2026-03-01 10:50:37 -05:00
missing fix(ui): export all missing files instead of first 1000 2025-12-16 06:43:02 -05:00
personal fix(lastfm): require signed state token on link callback (#5521) 2026-05-23 12:16:15 -03:00
player fix(transcoding): honor player forced format on the WebUI transcode flow (#5613) 2026-06-14 20:52:19 -04:00
playlist fix(artwork): address WebP performance regression on low-power hardware (#5286) 2026-04-04 15:17:01 -04:00
plugin fix(ui): add Rescan button to plugin list empty state (#5471) 2026-05-05 18:49:24 -04:00
radio fix(artwork): address WebP performance regression on low-power hardware (#5286) 2026-04-04 15:17:01 -04:00
reducers feat(ui): replace UI scrobble with reportPlayback and redesign NowPlaying panel (#5448) 2026-05-01 15:27:32 -04:00
share fix(ui): allow scrolling in shareplayer queue by adding delay #4748 2025-11-29 12:54:46 -05:00
song perf(db): index media_file album/artist sort orders (#5706) 2026-07-03 08:58:55 -04:00
store feat: Multi-library support (#4181) 2025-07-18 18:41:12 -04:00
subsonic feat(ui): replace UI scrobble with reportPlayback and redesign NowPlaying panel (#5448) 2026-05-01 15:27:32 -04:00
themes fix(ui): Fix Nautiline theme font and width on mobile devices (#5590) 2026-06-10 16:47:54 -04:00
transcode fix(ui): improve browser codec detection and limit Safari transcoding to mp3 (#5171) 2026-03-12 08:21:49 -04:00
transcoding fix(ui): minor icon inconsistencies and "no missing files" translation (#3837) 2025-03-16 19:39:19 -04:00
user fix(ui): make self-service profile edits report their outcome (#5699) 2026-07-01 21:31:29 -04:00
utils fix(ui): use div for fragment, check lastfm url for artist page (#4980) 2026-02-04 17:34:26 -05:00
App.jsx fix(ui): load ND_DEFAULTLANGUAGE on app startup (#4000) 2026-06-09 19:27:15 -04:00
authProvider.js feat(server): add ExtAuth logout URL configuration (#5074) 2026-02-23 20:28:38 -05:00
config.js feat(ui): replace UI scrobble with reportPlayback and redesign NowPlaying panel (#5448) 2026-05-01 15:27:32 -04:00
consts.js fix(artwork): address WebP performance regression on low-power hardware (#5286) 2026-04-04 15:17:01 -04:00
eventStream.js fix(ui): keep the NowPlayingPanel badge in sync. 2025-06-29 11:35:10 -04:00
eventStream.test.js chore(deps): update all dependencies (#4618) 2025-10-25 17:05:16 -04:00
hotkeys.js Change "Go to current song" hotkey. 2022-12-18 20:58:01 -05:00
index.css chore(deps): bump JS dependencies 2025-03-07 19:47:08 -05:00
index.jsx fix(ui): service worker does not load new version of ui (#3402) 2024-10-16 21:39:14 -04:00
routes.jsx build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
setupTests.js Use the default import path for jest-dom. 2024-01-17 17:07:43 -05:00
sw.js fix(ui): service worker crashing on precacheAndRoute (#3528) 2024-12-08 17:43:34 -05:00
useChangeThemeColor.jsx build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00
vite-env.d.ts build(ui): migrate from CRA/Jest to Vite/Vitest (#3311) 2024-09-28 11:54:36 -04:00