fix(namespace): the JS sorted fallback honors the ruled ordering contract — nulls last in BOTH directions (was nulls-first on desc) + deterministic id-ascending tie-break
This commit is contained in:
parent
d8d0b55f9d
commit
56deb2e888
1 changed files with 19 additions and 9 deletions
|
|
@ -2260,20 +2260,30 @@ export class MetadataIndexManager implements MetadataIndexProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
idValuePairs.sort((a, b) => {
|
idValuePairs.sort((a, b) => {
|
||||||
if (a.value == null && b.value == null) return 0
|
// Ordering contract (cross-engine, ruled 2026-08-03): missing/null
|
||||||
if (a.value == null) return order === 'asc' ? 1 : -1
|
// values sort LAST in BOTH directions — the direction flip never moves
|
||||||
if (b.value == null) return order === 'asc' ? -1 : 1
|
// them to the front — and ties break by id ascending, so an ordered
|
||||||
if (a.value === b.value) return 0
|
// read is deterministic and identical on both engines. Rows are never
|
||||||
|
// dropped for lacking the field.
|
||||||
|
const aNull = a.value == null
|
||||||
|
const bNull = b.value == null
|
||||||
|
if (aNull || bNull) {
|
||||||
|
if (aNull && bNull) return a.id < b.id ? -1 : a.id > b.id ? 1 : 0
|
||||||
|
return aNull ? 1 : -1
|
||||||
|
}
|
||||||
// Numbers compare numerically; everything else by code-point (UTF-8 byte) order.
|
// Numbers compare numerically; everything else by code-point (UTF-8 byte) order.
|
||||||
// This makes the JS fallback sort match cor's native column store exactly
|
// This makes the JS fallback sort match cor's native column store exactly
|
||||||
// (numeric i64/f64 vs code-point strings) and stay deterministic across
|
// (numeric i64/f64 vs code-point strings) and stay deterministic across
|
||||||
// environments, unlike the `<` operator's UTF-16 ordering for strings.
|
// environments, unlike the `<` operator's UTF-16 ordering for strings.
|
||||||
let comparison: number
|
let comparison = 0
|
||||||
if (typeof a.value === 'number' && typeof b.value === 'number') {
|
if (a.value !== b.value) {
|
||||||
comparison = a.value < b.value ? -1 : 1
|
if (typeof a.value === 'number' && typeof b.value === 'number') {
|
||||||
} else {
|
comparison = a.value < b.value ? -1 : 1
|
||||||
comparison = compareCodePoints(String(a.value), String(b.value))
|
} else {
|
||||||
|
comparison = compareCodePoints(String(a.value), String(b.value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (comparison === 0) return a.id < b.id ? -1 : a.id > b.id ? 1 : 0
|
||||||
return order === 'asc' ? comparison : -comparison
|
return order === 'asc' ? comparison : -comparison
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue