perf(smartplaylist): batch-load genres instead of per-row correlated subquery
Evaluate now issues a lean main SELECT over the joined metadata tables with no genre column, then batch-fetches genres with a single query using WHERE recording_id IN (...). Previously the track_metadata view's correlated GROUP_CONCAT subquery ran per row and scaled with library size rather than result size, producing multi-second load times for 100-track smart playlists. - Inline the metadata joins instead of using the track_metadata view, so the per-row GROUP_CONCAT never runs on the hot path. Other callers of the view (search, library listing) are unaffected. - Route all genre operators (is/is_not/is_any_of/contains/etc.) through a recording_genres subquery against af.recording_id. Previously text operators like "contains" matched against the view's concatenated genre column, which is no longer in scope. - Sort-by-genre falls back to Go-side sort after the batch genre merge since there is no single SQL column to sort on. - Log main_ms / genres_ms / total_ms at Debug for future tuning. - Add (*DB).Logger() accessor so smartplaylist can reuse the DB's structured logger without changing Evaluate's signature. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -148,6 +148,12 @@ func (d *DB) QueryContext(query string, args ...any) (*sql.Rows, error) {
|
||||
return d.db.QueryContext(d.Ctx, query, args...)
|
||||
}
|
||||
|
||||
// Logger returns the structured logger bound to this DB. Callers can
|
||||
// use it to emit timing or diagnostic logs from query-adjacent code.
|
||||
func (d *DB) Logger() *slog.Logger {
|
||||
return d.logger
|
||||
}
|
||||
|
||||
// applyPRAGMAs configures SQLite connection settings. Called by both
|
||||
// NewDB and NewTestDB to ensure identical behavior.
|
||||
func applyPRAGMAs(ctx context.Context, db *sql.DB) error {
|
||||
@@ -1222,7 +1228,7 @@ func migration9SmartPlaylists(
|
||||
// migration10PlayHistory adds play history tracking:
|
||||
// - play_history table for timestamped play log
|
||||
// - play_count and last_played columns on audio_files
|
||||
// - Recreates track_metadata VIEW to expose the new columns
|
||||
// - Recreates track_metadata VIEW to expose the new columns.
|
||||
func migration10PlayHistory(
|
||||
ctx context.Context,
|
||||
db *sql.DB,
|
||||
|
||||
Reference in New Issue
Block a user