fix: orderable fractional indexing case-sensitivity issue with PostgreSQL#14867
fix: orderable fractional indexing case-sensitivity issue with PostgreSQL#14867paulpopus merged 4 commits intopayloadcms:mainfrom
Conversation
DanRibbens
left a comment
There was a problem hiding this comment.
Thanks for the PR! I actually thought that we fixed this some time ago.
Assuming all existing tests pass, just a few things need to happen before this can be merged.
- remove the excessive comments generated by AI.
- add test in
test/sort/int.spec.ts
| }) | ||
| const orders = (related.orderableJoinField1 as { docs: Orderable[] }).docs.map((doc) => | ||
| parseInt(doc._orderable_orderableJoinField1_order, 16), | ||
| parseInt(doc._orderable_orderableJoinField1_order, 36), |
There was a problem hiding this comment.
Using base 36 because fractional indexing keys use characters 0-9 and a-z.
Base 16 would fail for keys like '9z' (returning just 9) since 'z' is not a valid hex character.
Also changed it in other tests for cohesion, it breaks nothing.
|
The ordering still broken, when will this issue is resolved :( |
|
@hohoaisan sorry about that, will bring this up to the team |
|
Some of us generates the order outside of payload admin system and would need a changed function like this to be exported. |
|
Exporting the utilities here #15286 |
|
To the best of my knowledge and understanding, Postgres's sort actually is case sensitive by default. This feels to me like it may be a case of AI hallucination? When I have an orderable field in Payload and open the database in my database client and sort by the EDIT: After further digging, I think it may be OS dependent. Postgres seems to defer to the OS on this particular case, and there are differences between macOS and Debian (and perhaps other Linux distros as well). |
|
🚀 This is included in version v3.73.0 |
fix: orderable fractional indexing case-sensitivity issue with PostgreSQL
Problem
When using the
orderablefeature with PostgreSQL, reordering documents to the "first" position generates keys that break subsequent reordering operations.Scenario
a0,a1a1beforea0(to make it first)Zzfor the moved documentRoot Cause
The fractional indexing algorithm uses:
A-Zfor "smaller" integer keysa-zfor "larger" integer keysThis relies on ASCII ordering where
'Z'(code 90) <'a'(code 97), soZz < a0.However, PostgreSQL's default collation (
en_US.UTF-8) uses case-insensitive comparison, treating'Z'as'z'. This means:Zz < a0✓Zztreated aszz, sozz > a0✗This mismatch causes:
generateKeyBetweenfunction receives arguments in wrong orderSolution
Modified the fractional indexing algorithm to use only characters that sort consistently across all database collations:
A-Z(uppercase)0-9(digits)a-z(lowercase)a-z(lowercase, unchanged)Key insight: Digits (
0-9) always sort before letters in both ASCII ordering and case-insensitive collations.Before (broken)
After (fixed)
Changes
packages/payload/src/config/orderable/fractional-indexing.jsa-zkeysA-Zkeys are still parsed (for backward compatibility) but won't be generatedBackward Compatibility
a-zcontinue to work correctlyA-Z(uppercase) will be parsed but may sort incorrectly in case-insensitive databases. Users with such keys should run a migration to regenerate them.Testing
Verified the algorithm produces correct ordering:
Related