Skip to content

Keep argument column order when a join swaps its sides - #86

Merged
clojure-finance merged 1 commit into
2.x.xfrom
join-column-order
Sep 25, 2026
Merged

clojure-finance merged 1 commit into
2.x.xfrom
join-column-order

Conversation

@buehlmaier

Copy link
Copy Markdown
Collaborator

Batch 3 of the agreed plan. inner-join and outer-join keep the swap-smaller-side heuristic (stream the larger input, group the smaller), but the swap no longer leaks into the output: without :select, headers, data, and preview now present the caller's column order (1_* first) instead of the second dataframe's (2_* first).

Mechanism: JoinedDataFrame gains a swapped flag, set only by the swapping branches of inner-join/outer-join. getColNames presents the caller's order, so :select name resolution and validation follow it for free. compute translates the user-facing select indices to internal ones (internal rows are always record-a then record-b columns) and feeds those to the existing write-index computation, which already reorders emitted values into select order — the join and outer-join workers are completely untouched.

Out of scope, unchanged: right-join's swap is semantic rather than a size heuristic, so its output order stays as is; rolling joins never swap.

Tests written first and seen failing on the 2_*-first order:

  • api-test (no Onyx): getColNames and preview on a swapped inner/outer join.
  • regression-test (Onyx): a join whose second input is larger, checked over disk and in-memory paths — full output for inner and outer, plus a :select on a swapped join.

No fixture changes needed: the existing join tests' first input is the larger file, and the swapped-order fixtures (1-6, 1-12) all use explicit :select. The plan's fixture note turned out unnecessary — the regression test writes its own inputs and asserts inline, like the #84 tests.

Suite: 52 tests / 198 assertions, green locally on JDK 25. No version bump; rides with #83–#85 in a later 2.0.6.

inner-join and outer-join stream the larger input and group the smaller
one, swapping a and b when b's file is bigger. The swap leaked into the
output: without :select, the second dataframe's columns (2_*) came
first, in the header, the data, and preview.

JoinedDataFrame gains a swapped flag, set only by the swapping branches
of inner-join and outer-join:

- getColNames presents the caller's order (record b first when swapped),
  so headers, preview and :select name resolution all follow it.
- compute translates the user-facing select indices to internal ones
  (rows are always record-a columns then record-b columns); the existing
  write-index machinery then reorders the emitted values, so the join
  workers are untouched.
- preview maps each side's old names onto the right half of the new
  ones.

right-join keeps its existing swapped output order (its swap is
semantic, not a size heuristic), and rolling joins never swap.

Tests written first and seen failing on the 2_*-first order: a fast
api-test for getColNames and preview, and an Onyx-backed regression test
over disk and in-memory paths, including a :select on a swapped join.
Existing fixtures are unaffected: the join tests' first input is the
larger file, and the swapped-order fixtures all use :select.
@clojure-finance
clojure-finance merged commit 5574231 into 2.x.x Sep 25, 2026
3 checks passed
@buehlmaier
buehlmaier deleted the join-column-order branch September 25, 2026 03:13
@buehlmaier buehlmaier mentioned this pull request Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants