feat(series): add SQL queries for series browsing and continue-series

Add five new sqlc queries to support the series browse page and
continue-series dashboard collection:

- GetDistinctSeries: list unique series with book counts, sorted by
  most recent entry, with pagination
- GetDistinctSeriesCount: total distinct series count for pagination
- GetSeriesCovers: fetch up to N cover image paths for a series,
  ordered by series_number
- GetSeriesBooks: fetch all books in a series ordered by series_number
- GetContinueSeriesItems: CTE-based query using DISTINCT ON to find
  the next unread book per series for a given user/library, sorted
  by most recent last_read_at
This commit is contained in:
2026-05-08 20:26:39 -04:00
parent ed68f92f4a
commit d48404e800
3 changed files with 430 additions and 0 deletions
+53
View File
@@ -1905,6 +1905,59 @@ SELECT mi.* FROM media_items mi
WHERE mi.library_id = $1
ORDER BY mi.created_at DESC;
-- name: GetDistinctSeries :many
SELECT series, COUNT(*) as book_count,
MAX(series_count) as total_in_series,
MAX(created_at) as last_entry_at
FROM media_items
WHERE library_id = $1 AND series IS NOT NULL AND series != ''
GROUP BY series
ORDER BY MAX(created_at) DESC
LIMIT $2 OFFSET $3;
-- name: GetDistinctSeriesCount :one
SELECT COUNT(DISTINCT series)::int
FROM media_items
WHERE library_id = $1 AND series IS NOT NULL AND series != '';
-- name: GetSeriesCovers :many
SELECT cover_image_path, library_id
FROM media_items
WHERE library_id = $1 AND series = $2 AND cover_image_path IS NOT NULL AND cover_image_path != ''
ORDER BY series_number ASC NULLS LAST
LIMIT $3;
-- name: GetSeriesBooks :many
SELECT * FROM media_items
WHERE library_id = $1 AND series = $2
ORDER BY series_number ASC NULLS LAST;
-- name: GetContinueSeriesItems :many
WITH user_series_progress AS (
SELECT mi.series,
MAX(mi.series_number) as max_read_number,
MAX(rp.last_read_at) as last_read_at
FROM reading_progress rp
JOIN media_items mi ON mi.id = rp.media_item_id
WHERE rp.user_id = $2
AND rp.percentage > 0
AND mi.series IS NOT NULL AND mi.series != ''
AND mi.library_id = $1
GROUP BY mi.series
),
next_books AS (
SELECT DISTINCT ON (mi.series) mi.*,
usp.last_read_at
FROM media_items mi
JOIN user_series_progress usp ON mi.series = usp.series
WHERE mi.library_id = $1
AND (mi.series_number > usp.max_read_number OR usp.max_read_number IS NULL)
ORDER BY mi.series, mi.series_number ASC NULLS LAST
)
SELECT * FROM next_books
ORDER BY last_read_at DESC NULLS LAST
LIMIT $3;
-- name: GetSavedFilters :many
SELECT * FROM saved_filters
WHERE user_id = @user_id AND resource_type = @resource_type