Merge the Alternate Number sort into the Alternate Series sort - #832
Merged
Conversation
One sort key now does both halves: comics group by their alternate series (ComicInfo AlternateSeries / MetronInfo reprints) and order by the parsed AlternateNumber inside the group, falling back to the comic's own series and issue so untagged comics interleave. A correlated subquery elects one reprint per comic by natural tuple order and ORDER BY is a five-field Coalesce list reading from that election — numbers compare numerically, and the shared election ordering keeps the tuple atomic (the old sort aggregated number and suffix independently and could pair parts from different reprints). An active reprints filter narrows the election instead of using a FilteredRelation. Benchmarked at parity with a join+aggregate at 120k comics; the fallback series segment uses lower(name) so article-led titles interleave with their alternate namesakes. Collection rows sort by a matching fixed-width key in the intersection RawSQL, with an in-SQL fallback to the collection's own name. The key reads only reprint columns (a per-comic fallback under GROUP BY came back from an arbitrary row) and its printf formats are %%-escaped so the DEBUG query logger doesn't consume bound params. alternate_number is removed from the order-by choices (0053 edited in place, unreleased). New 0054 remaps stored settings in order_by, order_extra_keys and collection_order_memory with dedupe; sidecar restore gets a sort-key rename map covering the same three homes. Cover-pick subqueries for the merged key normalize to sort_name like every other M2M sort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The two half-working sorts for alternate series are now one key: Alternate Series (
reprints). Comics group by their alternate series identity (name, volume, language) and order by the parsed AlternateNumber inside the group — #2 before #10 — falling back to the comic's own series and issue so untagged comics interleave instead of clumping.alternate_numberis removed from the order-by choices with a full stored-settings remap. Matches the reader, which already reads alternate series in parsed-number order.Why
Previously each sort did half the job: Alternate Series ordered by the display label, so "#10" sorted before "#2" and fallback comics had no issue tail; Alternate Number sorted numerically but only resolved which reprint via the reprints filter, silently degraded to the plain issue sort without it, and never considered the series name. The merge also fixes two latent bugs: the label sort compared case-sensitively (SQLite collation doesn't survive
Coalesce/Concat/Min), and the filtered number sort aggregated number and suffix independently, so a comic with reprints "#2" and "#10a" sorted by a fabricated "#2a".How
columns.py): a correlated subquery elects one reprint per comic by natural tuple order; ORDER BY is a five-field Coalesce list reading from that election. All five subqueries share one election ordering, so tied rows carry identical values — the tuple stays atomic without a composed string key. An active reprints filter narrows the election (pk__in); no moreFilteredRelation. Benchmarked at parity with the join+aggregate shape (164 vs 167 ms sorting 120k comics; the forcedGROUP BY pkkeeps the correlated subqueries out of GROUP BY). The fallback series segment islower(name)rather than the article-movedsort_name, so an untagged "The Batman" interleaves with its "The Batman" alternate namesake.intersections.py): the intersection RawSQL emits a matching fixed-width key with an in-SQL fallback to the collection's own name (a DjangoCoalescewrapper would re-trigger the quadratic Folder GROUP BY regression). The key reads only reprint columns — a per-comic fallback underGROUP BY target_idreturned an arbitrary child's issue — and its printf formats are%%-escaped so the DEBUG query logger (bin/dev.shdefault) doesn't consume bound params. Both fixes have regression tests.alternate_number → reprintsinorder_by,order_extra_keys, andcollection_order_memorywith dedupe (forward-only; the merge is lossy). Sidecar restore gets a separate sort-key rename map covering the same three homes — kept apart from_LEGACY_KEY_RENAMES, which also drives filter-column resolution.alternate_numbercaption branch is gone;formatReprintsand theorder_valuedisplay contract are unchanged.Reviewer notes
sort_namein cover subqueries like every other M2M sort); the reprints filter no longer influences collection-row ordering.reprintsstays a legal multi-sort extra;Comic.alternative_issue_numberis an unrelated display-only field and is untouched.test_browser_reprints_column.pyincluding cross-series election atomicity, case folding, article interleave, and a DEBUG-cursor regression; 0054 remap and restore-rename suites. The subtle guarantees were mutation-tested (each fails a dedicated test when broken). Full suite 1032 passed; lint/ty/complexity clean.🤖 Generated with Claude Code