2b286a9bfd
Production list-playlists handler reuses Service.List + in-memory kind filter so other users' public playlists can surface alongside the caller's filtered own. Comment clarifies the divergence so future readers don't expect this query to be wired into the API path.
157 lines
5.4 KiB
SQL
157 lines
5.4 KiB
SQL
-- M7 #352 slice 2: system-generated playlist queries.
|
|
|
|
-- name: ListActiveUsersForSystemPlaylists :many
|
|
-- Active = had a play in the last 7 days. The cron iterates this list.
|
|
SELECT u.id FROM users u
|
|
WHERE EXISTS (
|
|
SELECT 1 FROM play_events pe
|
|
WHERE pe.user_id = u.id
|
|
AND pe.started_at > now() - INTERVAL '7 days'
|
|
);
|
|
|
|
-- name: GetSystemPlaylistRun :one
|
|
SELECT user_id, last_run_at, last_run_date, in_flight, last_error
|
|
FROM system_playlist_runs
|
|
WHERE user_id = $1;
|
|
|
|
-- name: TryClaimSystemPlaylistRun :one
|
|
-- Atomic test-and-set on in_flight. Returns the row if we won the claim,
|
|
-- or pgx.ErrNoRows if someone else is already building.
|
|
INSERT INTO system_playlist_runs (user_id, in_flight)
|
|
VALUES ($1, true)
|
|
ON CONFLICT (user_id) DO UPDATE
|
|
SET in_flight = true
|
|
WHERE system_playlist_runs.in_flight = false
|
|
RETURNING user_id, last_run_at, last_run_date, in_flight, last_error;
|
|
|
|
-- name: FinishSystemPlaylistRun :exec
|
|
-- Mark a successful build complete.
|
|
UPDATE system_playlist_runs
|
|
SET last_run_at = $2,
|
|
last_run_date = $3,
|
|
in_flight = false,
|
|
last_error = NULL
|
|
WHERE user_id = $1;
|
|
|
|
-- name: FailSystemPlaylistRun :exec
|
|
-- Mark a failed build complete (in_flight=false, error captured).
|
|
UPDATE system_playlist_runs
|
|
SET in_flight = false,
|
|
last_error = $2
|
|
WHERE user_id = $1;
|
|
|
|
-- name: ClearStaleSystemPlaylistInFlight :exec
|
|
-- Cron startup recovery: clear any in_flight=true rows from a previously
|
|
-- crashed process. Safe because at startup, by definition, nothing is
|
|
-- still building.
|
|
UPDATE system_playlist_runs SET in_flight = false WHERE in_flight = true;
|
|
|
|
-- name: PickSeedArtists :many
|
|
-- Top-3 most-engaged distinct artists in the user's last 7 days.
|
|
-- Score = unskipped-play count + 5 if user has liked the artist.
|
|
WITH plays AS (
|
|
SELECT t.artist_id,
|
|
COUNT(*) FILTER (WHERE pe.was_skipped = false) AS play_count
|
|
FROM play_events pe
|
|
JOIN tracks t ON t.id = pe.track_id
|
|
WHERE pe.user_id = $1
|
|
AND pe.started_at > now() - INTERVAL '7 days'
|
|
AND t.artist_id IS NOT NULL
|
|
GROUP BY t.artist_id
|
|
),
|
|
liked AS (
|
|
SELECT artist_id FROM general_likes_artists WHERE user_id = $1
|
|
)
|
|
SELECT p.artist_id,
|
|
(p.play_count + CASE WHEN l.artist_id IS NOT NULL THEN 5 ELSE 0 END)::bigint AS score
|
|
FROM plays p
|
|
LEFT JOIN liked l ON l.artist_id = p.artist_id
|
|
ORDER BY score DESC, p.artist_id
|
|
LIMIT 3;
|
|
|
|
-- name: PickTopPlayedTrackForUser :one
|
|
-- For-You seed selection. Returns the user's most-played non-skipped
|
|
-- track in the last 7 days; tie-break by track_id for determinism.
|
|
SELECT t.id
|
|
FROM play_events pe
|
|
JOIN tracks t ON t.id = pe.track_id
|
|
WHERE pe.user_id = $1
|
|
AND pe.started_at > now() - INTERVAL '7 days'
|
|
AND pe.was_skipped = false
|
|
GROUP BY t.id
|
|
ORDER BY COUNT(*) DESC, t.id
|
|
LIMIT 1;
|
|
|
|
-- name: PickTopPlayedTrackForArtistByUser :one
|
|
-- "Songs like X" seed selection. Returns the user's most-played non-skipped
|
|
-- track by artist X in the last 7 days. Falls back to the artist's most-
|
|
-- recent album's first track if no plays exist.
|
|
-- Cast to uuid so sqlc infers pgtype.UUID rather than interface{}.
|
|
SELECT COALESCE(
|
|
(SELECT t.id
|
|
FROM play_events pe
|
|
JOIN tracks t ON t.id = pe.track_id
|
|
WHERE pe.user_id = $1
|
|
AND t.artist_id = $2
|
|
AND pe.started_at > now() - INTERVAL '7 days'
|
|
AND pe.was_skipped = false
|
|
GROUP BY t.id
|
|
ORDER BY COUNT(*) DESC, t.id
|
|
LIMIT 1),
|
|
(SELECT t.id
|
|
FROM tracks t
|
|
JOIN albums a ON a.id = t.album_id
|
|
WHERE t.artist_id = $2
|
|
ORDER BY a.release_date DESC NULLS LAST,
|
|
t.disc_number NULLS LAST,
|
|
t.track_number NULLS LAST,
|
|
t.id
|
|
LIMIT 1)
|
|
)::uuid AS id;
|
|
|
|
-- name: PickTopAlbumCoverForArtistByUser :one
|
|
-- "Songs like X" cover. Most-played album by user; tie-break by most-recent
|
|
-- release. NULL if the artist has no albums or all covers are NULL.
|
|
-- Note: albums uses cover_art_path (not cover_path).
|
|
SELECT a.cover_art_path
|
|
FROM albums a
|
|
LEFT JOIN tracks t ON t.album_id = a.id
|
|
LEFT JOIN play_events pe
|
|
ON pe.track_id = t.id
|
|
AND pe.user_id = $1
|
|
AND pe.was_skipped = false
|
|
WHERE a.artist_id = $2
|
|
AND a.cover_art_path IS NOT NULL
|
|
GROUP BY a.id, a.release_date, a.cover_art_path
|
|
ORDER BY COUNT(pe.id) DESC, a.release_date DESC NULLS LAST, a.id
|
|
LIMIT 1;
|
|
|
|
-- name: DeleteSystemPlaylistsForUser :exec
|
|
-- Atomic-replace step. CASCADE on playlist_tracks.playlist_id handles
|
|
-- the join rows.
|
|
DELETE FROM playlists WHERE user_id = $1 AND kind = 'system';
|
|
|
|
-- name: ListPlaylistsByUserAndKind :many
|
|
-- Used in integration tests; production filter applied in Go
|
|
-- (api.handleListPlaylists fetches via Service.List and filters by Kind in
|
|
-- memory so it can also surface other users' public playlists alongside
|
|
-- the caller's own kind-filtered ones).
|
|
-- Sorted by updated_at DESC.
|
|
SELECT id, user_id, name, description, is_public, kind, system_variant,
|
|
seed_artist_id, cover_path, track_count, duration_sec,
|
|
created_at, updated_at
|
|
FROM playlists
|
|
WHERE user_id = $1
|
|
AND ($2::text = 'all' OR kind = $2)
|
|
ORDER BY updated_at DESC;
|
|
|
|
-- name: CreateSystemPlaylist :one
|
|
-- Inserts a system-generated playlist. Used by BuildSystemPlaylists.
|
|
-- For 'for_you' variant, pass seed_artist_id as NULL (zero-value pgtype.UUID
|
|
-- with Valid=false).
|
|
INSERT INTO playlists (
|
|
user_id, name, description, is_public,
|
|
kind, system_variant, seed_artist_id, cover_path
|
|
) VALUES ($1, $2, '', false, 'system', $3, $4, $5)
|
|
RETURNING *;
|