Proposal: Opt-in field/key ordering preservation in query output #7387
msanchezdev
started this conversation in
Ideas
Replies: 2 comments
|
I really like this, if theres no noticeable overhead/breaking change which i would imagine its pretty safe, then i would just have this on all the time as theres multiple times where im grouping fields based on their context and it becomes annoying looking through 30 fields when i know my order i want, ive resulted in |
0 replies
|
Instead of adding the clause to the end, why not make it part of the
|
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Summary
Let SurrealDB return object fields in the order the query expressed them, instead of always alphabetically — as an opt-in, default-off policy that leaves today's sorted behaviour unchanged.
#2715 was closed on the grounds that objects are stored as sorted B-trees, so output is necessarily sorted. That's true — and this proposal does not change it. The sorted form stays fully canonical for storage and comparison; field order is carried as presentation-only metadata. The PoC below proves this is both possible and cheap.
Why now (what changed since #2715)
Field order is a presentation concern that can ride alongside the canonical sorted value without altering it:
Ord/Eq/Hashstay defined on the sorted form — so comparison, dedup, index keys, and complex Record IDs (Bug: The alphabetical order of the properties of an object-based Record ID is query-significant #4053) are unchanged.Correctness guardrail (#4053)
#4053 established that object-key order is load-bearing: complex Record IDs sort object keys before storage, which is what makes range scans correct. This proposal preserves that exactly — the ordering metadata is excluded from
Ord/Eq/Hashand never touches Record IDs or keys. It is structurally impossible for this feature to change comparison or storage semantics. (The PoC has a test asserting two objects that differ only in display order remain==and hash-equal.)Proposed design
1. Presentation-order side-car on
Object. The value keeps its canonical sorted map and gains an optional display-order permutation.None= today's behaviour, zero overhead. To guarantee byte-identical encoding, the PoC keeps the existing revisioned/storekey map as an inner type and delegates to it — nothing about the wire format is re-implemented.2. Opt-in surface (two tiers, default off):
--allow-experimental field_orderinglets an operator enable the feature at all.DEFINE CONFIG [OVERWRITE | IF NOT EXISTS] FIELDS ORDER [ SORTED | WRITTEN | DECLARED ], introspectable viaINFO FOR DB(round-trips like the GraphQL/API configs; needs matchingALTER/REMOVE CONFIG).A 3-valued enum, not a boolean, future-proofs the lifecycle: today the default is
SORTED; later, flipping the default makes the same surface an opt-out — no new grammar, no breaking change. (AnOPTION-style flag was considered and rejected: it's a per-connection execution mode, can't carry an order, and can't name three states.)3. Per-query override —
PRESERVE ORDER(a trailing clause):Forces written order for one statement. Placed at the end (after
FETCH/TIMEOUT), so it never collides withORDER BY— the PoC parses both in the same query (… ORDER BY name LIMIT 5 PRESERVE ORDER) without ambiguity.4. Semantics — "how to order fields the query did not explicitly list":
SORTED— today, alphabetical everywhere.SORTED— explicit projections/literals (SELECT b, a,{ b, a },RETURN {…}) always honour written order.WRITTENvsDECLAREDdiffer only for implicitSELECT *of schemafull tables: insertion order vsDEFINE FIELDdeclaration order.5. Phased rollout (grammar accepts all enum values from PR 1; behaviour rolls out per phase):
SELECT b, a)RETURN+ write echoDECLARED)SELECT *viaDEFINE FIELDorderWRITTENpersisted)Proof of concept
A working PoC backs this discussion (#7386). It implements Phase A end-to-end behind the
field_orderingcapability.Engine (5 passing tests):
storekey_encoding_is_byte_identical— an object with a display-order side-car encodes to byte-for-byte the same bytes as without one, and round-trips back to the canonical value.order_excluded_from_eq_and_hash— objects differing only in display order are==, hash-equal,Ord-equal.display_order_is_preserved,preserve_order_sets_written_display_order,parse_select_preserve_order— the operator applies written order and the clause parses + round-trips.Live (HTTP
/sql):Honest scope of the PoC: only explicit projections reorder (Phase A);
SELECT */record echoes/literals are Phases B/C. On output, only the JSON encoder was made order-aware — as a shortcut it leans onserde_json'spreserve_orderfeature, but a production version would use a dedicated ordered JSON encoder and avoid any global change (noted inline in the PoC). CBOR/flatbuffers wire formats were left sorted. None of it touches storage or comparison.Non-goals (for now)
Open questions (for discussion)
Proposer's recommendation in italics; these are prompts, not blockers.
WRITTENto default? (Ship opt-in; the enum makes a later flip non-breaking.)FIELDS ORDER <enum>vsFIELD_ORDER/ORDERING? (A namespacedFIELDSblock at database scope, matchingDEFINE CONFIG GRAPHQL; a root-default/db-override layering is a possible follow-up.)Related: revisits #2715; honours the invariant documented in #4053.
All reactions