-- name: ListGenresWithCount :many -- Genre browse index (#367). -- -- Genres live inline on tracks.genre as a delimited string, so this splits on -- the same [;,] pattern already used by recommendation.sql and discover.sql — -- a track tagged "Rock;Pop" must count toward both, and diverging from the -- established pattern here would make the browse surface disagree with what -- the recommendation engine believes the library contains. -- -- trim() but deliberately NO lower(): trimming repairs an artifact of OUR -- splitting ("Rock; Pop" yields " Pop", and showing that as a distinct genre -- would be a bug), whereas case is what the tag actually says. Raw ID3 is -- exposed as-is for v1, so "Rock" and "rock" appear as separate rows. -- -- COUNT(DISTINCT) because a sloppy tag like "Rock;Rock" would otherwise -- inflate its own row. -- -- Ordered by count first: raw ID3 data has a long tail of one-off junk tags, -- so alphabetical would bury the handful of genres an operator actually has a -- library's worth of. Name breaks ties for a stable order. SELECT trim(g.genre) AS genre, COUNT(DISTINCT tracks.id)::bigint AS track_count FROM tracks JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true WHERE trim(g.genre) <> '' GROUP BY trim(g.genre) -- Ordered by the expression, not the output alias: `ORDER BY genre` is -- ambiguous between the alias and tracks.genre, and sqlc rejects it. ORDER BY track_count DESC, trim(g.genre); -- name: ListAlbumsByGenreWithArtist :many -- Albums for one genre, joined with artist_name for the browse grid. -- An album belongs to a genre when ANY of its tracks carry it. Splits and -- trims identically to ListGenresWithCount — if the list is built by -- splitting and the detail matched exactly, every multi-genre track would -- produce a genre row that leads to an empty page. SELECT sqlc.embed(albums), artists.name AS artist_name FROM albums JOIN artists ON artists.id = albums.artist_id WHERE EXISTS ( SELECT 1 FROM tracks JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true WHERE tracks.album_id = albums.id AND trim(g.genre) = trim(sqlc.arg(genre)::text) ) ORDER BY albums.sort_title, albums.id LIMIT sqlc.arg(lim) OFFSET sqlc.arg(off); -- name: CountAlbumsByGenre :one -- Total for the paging envelope. EXISTS mirrors the list query exactly; a -- JOIN + DISTINCT here would count differently the moment an album has two -- tracks carrying the same genre. SELECT COUNT(*) FROM albums WHERE EXISTS ( SELECT 1 FROM tracks JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true WHERE tracks.album_id = albums.id AND trim(g.genre) = trim(sqlc.arg(genre)::text) ); -- name: ListAlbumYearsWithCount :many -- Year browse index (#367). Only albums with a release_date appear — an -- album with no date isn't "year unknown" as a browsable bucket, it's absent -- from this axis, and the UI says so rather than inventing a 0 row. -- Newest first: recent releases are the likelier browse target. SELECT EXTRACT(YEAR FROM release_date)::int AS year, COUNT(*)::bigint AS album_count FROM albums WHERE release_date IS NOT NULL GROUP BY year ORDER BY year DESC; -- name: ListAlbumsByYearRangeWithArtist :many -- Albums released within an inclusive year range, for the albums-page filter. SELECT sqlc.embed(albums), artists.name AS artist_name FROM albums JOIN artists ON artists.id = albums.artist_id WHERE albums.release_date IS NOT NULL AND EXTRACT(YEAR FROM albums.release_date)::int BETWEEN sqlc.arg(year_from)::int AND sqlc.arg(year_to)::int ORDER BY albums.sort_title, albums.id LIMIT sqlc.arg(lim) OFFSET sqlc.arg(off); -- name: CountAlbumsByYearRange :one SELECT COUNT(*) FROM albums WHERE release_date IS NOT NULL AND EXTRACT(YEAR FROM release_date)::int BETWEEN sqlc.arg(year_from)::int AND sqlc.arg(year_to)::int; -- name: ListGenresForAlbum :many -- Distinct genres carried by an album's tracks, for the album detail page's -- quick-jump chips. Split and trimmed identically to ListGenresWithCount, so a -- chip always leads to a page that actually contains this album — the two -- diverging is exactly the bug #367 had to fix in ListAlbumsByGenre. SELECT DISTINCT trim(g.genre) AS genre FROM tracks JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true WHERE tracks.album_id = $1 AND trim(g.genre) <> '' ORDER BY trim(g.genre); -- name: ListGenresForArtist :many -- Same, across everything by one artist. Alphabetical rather than by count: -- an artist's genre set is small, and a stable order reads better than a -- frequency ranking nobody asked about. SELECT DISTINCT trim(g.genre) AS genre FROM tracks JOIN LATERAL regexp_split_to_table(coalesce(tracks.genre, ''), '[;,]') AS g(genre) ON true WHERE tracks.artist_id = $1 AND trim(g.genre) <> '' ORDER BY trim(g.genre);