From a085924f8a3246afa0a10dba778ede511e28a6c3 Mon Sep 17 00:00:00 2001 From: Froidefond Julien Date: Mon, 16 Mar 2026 12:16:00 +0100 Subject: [PATCH] fix(books): correction du tri naturel des titres avec sous-titres variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remplace REGEXP_REPLACE(title, '[0-9]+', '', 'g') par REGEXP_REPLACE(title, '[0-9].*$', '') pour n'extraire que le préfixe avant le premier chiffre. L'ancienne regex supprimait TOUS les chiffres du titre entier, y compris dans les sous-titres. Quand chaque volume a un sous-titre différent (ex: "Deadpool marvel deluxe 3 - Je suis ton homme"), la partie texte restante variait → l'ordre alphabétique des sous-titres prenait le dessus. Avec le nouveau préfixe "deadpool marvel deluxe " identique pour tous, le numéro de volume (2ème clé) dicte correctement l'ordre. Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/books.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/api/src/books.rs b/apps/api/src/books.rs index 3f72879..43c5dbe 100644 --- a/apps/api/src/books.rs +++ b/apps/api/src/books.rs @@ -141,7 +141,7 @@ pub async fn list_books( let order_clause = if query.sort.as_deref() == Some("latest") { "b.updated_at DESC".to_string() } else { - "b.volume NULLS LAST, REGEXP_REPLACE(LOWER(b.title), '[0-9]+', '', 'g'), COALESCE((REGEXP_MATCH(LOWER(b.title), '\\d+'))[1]::int, 0), b.title ASC".to_string() + "b.volume NULLS LAST, REGEXP_REPLACE(LOWER(b.title), '[0-9].*$', ''), COALESCE((REGEXP_MATCH(LOWER(b.title), '\\d+'))[1]::int, 0), b.title ASC".to_string() }; // DATA: mêmes params filtre, puis $N+1=limit $N+2=offset @@ -401,7 +401,7 @@ pub async fn list_series( PARTITION BY COALESCE(NULLIF(series, ''), 'unclassified') ORDER BY volume NULLS LAST, - REGEXP_REPLACE(LOWER(title), '[0-9]+', '', 'g'), + REGEXP_REPLACE(LOWER(title), '[0-9].*$', ''), COALESCE((REGEXP_MATCH(LOWER(title), '\d+'))[1]::int, 0), title ASC ) as rn @@ -428,7 +428,7 @@ pub async fn list_series( {q_cond} {count_rs_cond} ORDER BY - REGEXP_REPLACE(LOWER(sc.name), '[0-9]+', '', 'g'), + REGEXP_REPLACE(LOWER(sc.name), '[0-9].*$', ''), COALESCE( (REGEXP_MATCH(LOWER(sc.name), '\d+'))[1]::int, 0 @@ -570,7 +570,7 @@ pub async fn list_all_series( let series_order_clause = if query.sort.as_deref() == Some("latest") { "sc.latest_updated_at DESC".to_string() } else { - "REGEXP_REPLACE(LOWER(sc.name), '[0-9]+', '', 'g'), COALESCE((REGEXP_MATCH(LOWER(sc.name), '\\d+'))[1]::int, 0), sc.name ASC".to_string() + "REGEXP_REPLACE(LOWER(sc.name), '[0-9].*$', ''), COALESCE((REGEXP_MATCH(LOWER(sc.name), '\\d+'))[1]::int, 0), sc.name ASC".to_string() }; let limit_p = p + 1; @@ -588,7 +588,7 @@ pub async fn list_all_series( PARTITION BY COALESCE(NULLIF(series, ''), 'unclassified') ORDER BY volume NULLS LAST, - REGEXP_REPLACE(LOWER(title), '[0-9]+', '', 'g'), + REGEXP_REPLACE(LOWER(title), '[0-9].*$', ''), COALESCE((REGEXP_MATCH(LOWER(title), '\d+'))[1]::int, 0), title ASC ) as rn @@ -717,7 +717,7 @@ pub async fn ongoing_series( PARTITION BY COALESCE(NULLIF(series, ''), 'unclassified') ORDER BY volume NULLS LAST, - REGEXP_REPLACE(LOWER(title), '[0-9]+', '', 'g'), + REGEXP_REPLACE(LOWER(title), '[0-9].*$', ''), COALESCE((REGEXP_MATCH(LOWER(title), '\d+'))[1]::int, 0), title ASC ) AS rn