Skip to content

Phase 0.2.A v1.1 — CrossTableFilter: Multi-Table Join Prototype (Node.js) #384

Description

@miranov25

Phase 0.2.A: CrossTableFilter — Multi-Table Join Prototype (Node.js)

Labels: testing, joins, cross-table, phase-0.2.A Architecture approval: ✅ 2026-02-14 (6/6 unanimous) Proposal: PHASE_0_2_A_v1.1_Proposal.md (full specification) Estimated effort: ~11.5h (budget 14h)


Motivation

RI currently supports only N:1 gather joins (child→parent lookup). Physics workflows require the reverse: select parents → see their children (1:N), cascade through hierarchies (1:N:M), and traverse junction tables (M:N).

The Full Join Brainstorming (5/5 internal + 6/6 Arch Team) established that all join cardinalities reduce to one new primitive: the inverse index. This phase prototypes and validates it in Node.js before client-side integration.

Consumers: RootInteractive, AliasDataFrame, RDataFrameDSL, GroupBy regression — all need the same primitive.

What

Two core functions + test suite:

buildInverseIndex(fk_array, parent_count, options?)

Maps parent → children. Two variants:

  • InverseIndexCSR (unsorted FK): {offsets, indices} — standard CSR, always correct
  • SortedRangeIndex (sorted FK): {starts, counts} — contiguous ranges, cache-friendly

Both O(N) to build. Sentinels (default -1) excluded. Type-checked input.

CrossTableFilter.compute(inverse_index, selected_parents)

Returns Uint8Array include-mask (1=keep, 0=exclude) over child table. O(selected × avg_children).

AO2D-like test fixture (5 tables)

Collisions (100) → Tracks (5K, sorted FK) → Clusters (50K, sorted FK)
                 → V0s (200, multi-ref: posTrackId + negTrackId)
McLabels (5K, junction) ↔ McParticles (10K)
Sentinels: 5% tracks unassigned, 2% McLabels unassigned
Seeded PRNG (seed=42), parameterized sizes.

Test Matrix (15 cases)

ID Test Pattern
TC-INV-01 Build CSR — correctness Basic
TC-INV-02 Build SortedRange — correctness Basic
TC-INV-03 CSR vs SortedRange invariance Core invariance
TC-INV-04 Sentinel exclusion Boundary
TC-INV-05 Empty parent (0 children) Boundary
TC-CTF-01 Single parent selection Filter
TC-CTF-02 Range selection (10 parents) Filter
TC-CTF-03 Select all → all non-sentinel children Filter
TC-CTF-04 Select none → empty mask Filter
TC-CTF-05 3-level cascade (coll→tracks→clusters) Cascade
TC-CTF-06 M:N via junction (tracks↔MC particles) Junction
TC-CTF-07 Multi-reference (V0 pos/neg daughters) Multi-ref
TC-CTF-08 Round-trip invariance Invariance
TC-PERF-01 SortedRange vs CSR performance Benchmark
TC-COMPAT-01 N:1 lookup semantics unchanged Backward compat

Deliverables

test/crossTableFilter/
├── buildInverseIndex.mjs        # Core: CSR + SortedRangeIndex
├── CrossTableFilter.mjs         # Core: parent selection → child mask
├── ao2d_test_fixture.mjs        # 5-table generator
├── test_inverse_index.mjs       # TC-INV-01–05
├── test_cross_table_filter.mjs  # TC-CTF-01–08
├── test_performance.mjs         # TC-PERF-01
├── test_backward_compat.mjs     # TC-COMPAT-01
└── README.md

Environment: Node.js ≥ 18. Zero external dependencies.

Not in Scope

  • Common Layer 0 descriptor across RI/AliasDataFrame/RDataFrameDSL (Arch Team parallel)
  • Bokeh/browser integration (future phase)
  • Multi-table HistoNdCDS (Phase 2)
  • UI drill-down/breadcrumbs (Phase 4)
  • Bidirectional propagation child→parent (Phase 1b)

Definition of Done

  • buildInverseIndex correct for CSR and SortedRange (TC-INV-01, 02)
  • CSR ↔ SortedRange invariance for all parents (TC-INV-03)
  • Sentinels excluded, empty parents handled (TC-INV-04, 05)
  • CrossTableFilter correct for single/range/all/none (TC-CTF-01–04)
  • 3-level cascade correct (TC-CTF-05)
  • M:N junction traversal correct (TC-CTF-06)
  • Multi-reference correct (TC-CTF-07)
  • Round-trip invariance holds (TC-CTF-08)
  • SortedRange measurably faster than CSR (TC-PERF-01)
  • N:1 lookup semantics unchanged (TC-COMPAT-01)
  • All tests pass with node --test
  • Seeded, reproducible fixture (seed=42)
  • Zero npm dependencies
  • README with API docs + test matrix

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions