fix: interleave uploaded chapters by number in the chapter list
Uploaded chapters have no source_index, and ORDER BY source_index DESC NULLS LAST sorted every one of them after all crawled chapters regardless of number — an uploaded chapter 5 landed after crawled chapter 100. Slot each uploaded chapter by number, just before the crawled chapter with the smallest greater number, so it interleaves. Crawled rows keep their reversed source-DOM order, preserving the deliberate placement of non-numeric entries (migration 0021). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,14 +12,19 @@ pub async fn list_for_manga(
|
||||
limit: i64,
|
||||
offset: i64,
|
||||
) -> AppResult<Vec<Chapter>> {
|
||||
// Display order = source-site order reversed. The crawler stamps
|
||||
// `source_index` = position in the source DOM (0 = first = newest
|
||||
// on this site, see migration 0021), so DESC puts the oldest
|
||||
// chapter first and keeps the site's variant grouping and the
|
||||
// placement of non-numeric entries (e.g. "notice. : Officials")
|
||||
// intact. NULLS LAST keeps user-uploaded chapters (no source row)
|
||||
// and rows that pre-date the migration below crawled rows; the
|
||||
// (number, created_at) tail then orders them deterministically.
|
||||
// Display order. Crawled chapters carry `source_index` = position in the
|
||||
// source DOM (0 = newest on this site, migration 0021); we display them
|
||||
// reversed (oldest first) via the `-source_index` key, which keeps the
|
||||
// site's variant grouping and non-numeric entries (e.g. "notice.") in the
|
||||
// spot the site placed them — NOT clustered at number 0.
|
||||
//
|
||||
// User-uploaded chapters have no `source_index`. Instead of dumping them
|
||||
// after every crawled chapter (the old `NULLS LAST` bug, which put an
|
||||
// uploaded chapter 5 after crawled chapter 100), each is slotted by NUMBER:
|
||||
// just before the crawled chapter with the smallest number greater than it,
|
||||
// so it interleaves. An upload newer than every crawled chapter goes last;
|
||||
// when there are no crawled chapters at all, uploads fall back to the
|
||||
// `number, created_at` tail.
|
||||
let rows = sqlx::query_as::<_, Chapter>(
|
||||
r#"
|
||||
SELECT id, manga_id, number, title, page_count, created_at,
|
||||
@@ -31,7 +36,22 @@ pub async fn list_for_manga(
|
||||
FROM pages p WHERE p.chapter_id = chapters.id)::bigint AS size_bytes
|
||||
FROM chapters
|
||||
WHERE manga_id = $1
|
||||
ORDER BY source_index DESC NULLS LAST, number ASC, created_at ASC
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN source_index IS NOT NULL THEN (-source_index)::float8
|
||||
ELSE COALESCE(
|
||||
(SELECT MIN(-c2.source_index)::float8 - 0.5
|
||||
FROM chapters c2
|
||||
WHERE c2.manga_id = chapters.manga_id
|
||||
AND c2.source_index IS NOT NULL
|
||||
AND c2.number > chapters.number),
|
||||
(SELECT COALESCE(MAX(-c3.source_index), 0)::float8 + 0.5
|
||||
FROM chapters c3
|
||||
WHERE c3.manga_id = chapters.manga_id
|
||||
AND c3.source_index IS NOT NULL)
|
||||
)
|
||||
END,
|
||||
number ASC, created_at ASC
|
||||
LIMIT $2 OFFSET $3
|
||||
"#,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user