From 9cec32ba3e72a0ccd233326a209ecf169398e009 Mon Sep 17 00:00:00 2001 From: Froidefond Julien Date: Thu, 19 Mar 2026 12:56:02 +0100 Subject: [PATCH] fix: normalize series status casing to avoid duplicates - LOWER() all series_metadata.status values in the statuses endpoint to prevent "One shot" / "one shot" appearing as separate targets - Migration 0040: lowercase all existing status values in DB - Use LOWER() in series status filter queries for consistency Co-Authored-By: Claude Opus 4.6 --- apps/api/src/books.rs | 6 +++--- infra/migrations/0040_lowercase_series_status.sql | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 infra/migrations/0040_lowercase_series_status.sql diff --git a/apps/api/src/books.rs b/apps/api/src/books.rs index e5adcca..8b5df3b 100644 --- a/apps/api/src/books.rs +++ b/apps/api/src/books.rs @@ -398,7 +398,7 @@ pub async fn list_series( } else { String::new() }; let ss_cond = if query.series_status.is_some() { - p += 1; format!("AND sm.status = ${p}") + p += 1; format!("AND LOWER(sm.status) = ${p}") } else { String::new() }; let missing_cond = if has_missing { @@ -655,7 +655,7 @@ pub async fn list_all_series( } else { String::new() }; let ss_cond = if query.series_status.is_some() { - p += 1; format!("AND sm.status = ${p}") + p += 1; format!("AND LOWER(sm.status) = ${p}") } else { String::new() }; let missing_cond = if has_missing { @@ -868,7 +868,7 @@ pub async fn series_statuses( ) -> Result>, ApiError> { let rows: Vec = sqlx::query_scalar( r#"SELECT DISTINCT s FROM ( - SELECT status AS s FROM series_metadata WHERE status IS NOT NULL + SELECT LOWER(status) AS s FROM series_metadata WHERE status IS NOT NULL UNION SELECT mapped_status AS s FROM status_mappings ) t ORDER BY s"#, diff --git a/infra/migrations/0040_lowercase_series_status.sql b/infra/migrations/0040_lowercase_series_status.sql new file mode 100644 index 0000000..f2b6a47 --- /dev/null +++ b/infra/migrations/0040_lowercase_series_status.sql @@ -0,0 +1,5 @@ +-- Normalize all series_metadata.status values to lowercase for consistency. +-- This fixes case mismatches like "One shot" vs "one shot". +UPDATE series_metadata +SET status = LOWER(status), updated_at = NOW() +WHERE status IS NOT NULL AND status != LOWER(status);