Skip to content

feat: support aggregates in subqueries#1298

Merged
kevin-dp merged 9 commits intokevin/includesfrom
kevin/includes-aggregates
Mar 12, 2026
Merged

feat: support aggregates in subqueries#1298
kevin-dp merged 9 commits intokevin/includesfrom
kevin/includes-aggregates

Conversation

@kevin-dp
Copy link
Contributor

@kevin-dp kevin-dp commented Feb 25, 2026

Summary

  • Add tests for aggregates (e.g. count) in child/includes subqueries, asserting per-parent behavior
  • Fix processGroupBy to preserve __correlationKey through GROUP BY, so aggregates in child queries compute per-parent rather than globally

Problem

When using aggregates like count(i.id) inside an includes subquery, the correlation key that routes child results back to the correct parent was lost after the GROUP BY operator reshaped rows. This caused:

  1. Single-group aggregation (select with aggregates, no groupBy): all child rows across ALL parents aggregated into one global result
  2. Multi-group aggregation: rows from different parents with the same group key merged together
  3. Output extraction returned undefined for correlationKey, so results couldn't be routed to parent Collections

Approach

Unlike orderBy + limit (which required a new groupedOrderByWithFractionalIndex operator because limiting is a post-grouping operation that needs explicit per-parent partitioning), aggregates don't need a new "grouped groupBy" operator. The existing groupBy operator already handles per-parent aggregation naturally — we just need to include __correlationKey in the grouping key so that rows from different parents end up in separate groups.

Concretely:

  • Pass mainSource to processGroupBy when in includes mode
  • Include __correlationKey in the grouping key so rows from different parents stay in separate groups (e.g. { __singleGroup: true, __correlationKey: 1 } instead of just { __singleGroup: true })
  • Restore the { [mainSource]: { __correlationKey } } structure on output rows so the existing output extraction logic can route results back to the correct parent

Test plan

  • 6 new tests for aggregates in child queries (single-group as Collection/toArray, nested aggregates as Collection/toArray, reactivity on add/remove)
  • All 40 existing includes tests still pass
  • All 94 group-by tests still pass
  • Full test suite passes (1850/1850, 2 pre-existing failures in transactions.test.ts unrelated)

Note: The committed test file includes tests that fail without the fix. The implementation fix (in group-by.ts and index.ts) is in the working tree but not yet committed.

🤖 Generated with Claude Code

@changeset-bot
Copy link

changeset-bot bot commented Feb 25, 2026

🦋 Changeset detected

Latest commit: ac2022e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@tanstack/db Patch
@tanstack/angular-db Patch
@tanstack/electric-db-collection Patch
@tanstack/offline-transactions Patch
@tanstack/powersync-db-collection Patch
@tanstack/query-db-collection Patch
@tanstack/react-db Patch
@tanstack/rxdb-db-collection Patch
@tanstack/solid-db Patch
@tanstack/svelte-db Patch
@tanstack/trailbase-db-collection Patch
@tanstack/vue-db Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 25, 2026

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@1298

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@1298

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@1298

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@1298

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@1298

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@1298

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@1298

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@1298

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@1298

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@1298

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@1298

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@1298

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@1298

commit: fdfe4d1

@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

Size Change: +127 B (+0.13%)

Total Size: 98.6 kB

Filename Size Change
./packages/db/dist/esm/query/compiler/group-by.js 2.35 kB +111 B (+4.95%) 🔍
./packages/db/dist/esm/query/compiler/index.js 3.5 kB +16 B (+0.46%)
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.39 kB
./packages/db/dist/esm/collection/changes.js 1.22 kB
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/index.js 3.32 kB
./packages/db/dist/esm/collection/indexes.js 1.1 kB
./packages/db/dist/esm/collection/lifecycle.js 1.75 kB
./packages/db/dist/esm/collection/mutations.js 2.34 kB
./packages/db/dist/esm/collection/state.js 3.49 kB
./packages/db/dist/esm/collection/subscription.js 3.71 kB
./packages/db/dist/esm/collection/sync.js 2.41 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.83 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.74 kB
./packages/db/dist/esm/indexes/auto-index.js 742 B
./packages/db/dist/esm/indexes/base-index.js 766 B
./packages/db/dist/esm/indexes/btree-index.js 2.17 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.1 kB
./packages/db/dist/esm/indexes/reverse-index.js 538 B
./packages/db/dist/esm/local-only.js 808 B
./packages/db/dist/esm/local-storage.js 2.1 kB
./packages/db/dist/esm/optimistic-action.js 359 B
./packages/db/dist/esm/paced-mutations.js 496 B
./packages/db/dist/esm/proxy.js 3.75 kB
./packages/db/dist/esm/query/builder/functions.js 792 B
./packages/db/dist/esm/query/builder/index.js 5.15 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 1.05 kB
./packages/db/dist/esm/query/compiler/evaluators.js 1.43 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/joins.js 2.11 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.5 kB
./packages/db/dist/esm/query/compiler/select.js 1.11 kB
./packages/db/dist/esm/query/expression-helpers.js 1.43 kB
./packages/db/dist/esm/query/ir.js 784 B
./packages/db/dist/esm/query/live-query-collection.js 360 B
./packages/db/dist/esm/query/live/collection-config-builder.js 8.05 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 2.42 kB
./packages/db/dist/esm/query/live/internal.js 145 B
./packages/db/dist/esm/query/optimizer.js 2.62 kB
./packages/db/dist/esm/query/predicate-utils.js 2.97 kB
./packages/db/dist/esm/query/query-once.js 359 B
./packages/db/dist/esm/query/subset-dedupe.js 927 B
./packages/db/dist/esm/scheduler.js 1.3 kB
./packages/db/dist/esm/SortedMap.js 1.3 kB
./packages/db/dist/esm/strategies/debounceStrategy.js 247 B
./packages/db/dist/esm/strategies/queueStrategy.js 428 B
./packages/db/dist/esm/strategies/throttleStrategy.js 246 B
./packages/db/dist/esm/transactions.js 2.9 kB
./packages/db/dist/esm/utils.js 924 B
./packages/db/dist/esm/utils/browser-polyfills.js 304 B
./packages/db/dist/esm/utils/btree.js 5.61 kB
./packages/db/dist/esm/utils/comparison.js 952 B
./packages/db/dist/esm/utils/cursor.js 457 B
./packages/db/dist/esm/utils/index-optimization.js 1.51 kB
./packages/db/dist/esm/utils/type-guards.js 157 B

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

Size Change: 0 B

Total Size: 3.85 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 225 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.32 kB
./packages/react-db/dist/esm/useLiveQuery.js 1.34 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 559 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::react-db-package-size

@kevin-dp kevin-dp changed the title Support aggregates in child/includes queries feat: support aggregates in subqueries Feb 25, 2026
@kevin-dp kevin-dp force-pushed the kevin/includes-to-array branch 3 times, most recently from f491fd4 to 4c41062 Compare March 10, 2026 09:20
@kevin-dp kevin-dp force-pushed the kevin/includes-aggregates branch from bf9489e to 939f886 Compare March 10, 2026 10:30
Base automatically changed from kevin/includes-to-array to kevin/includes March 12, 2026 12:06
kevin-dp and others added 8 commits March 12, 2026 14:34
toArray() wraps an includes subquery so the parent row contains
Array<T> instead of Collection<T>. When children change, the parent
row is re-emitted with a fresh array snapshot.

- Add ToArrayWrapper class and toArray() function
- Add materializeAsArray flag to IncludesSubquery IR node
- Detect ToArrayWrapper in builder, pass flag through compiler
- Re-emit parent rows on child changes for toArray entries
- Add SelectValue type support for ToArrayWrapper
- Add tests for basic toArray, reactivity, ordering, and limits

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Test the reactive model difference between Collection and toArray includes:
- Collection includes: child change does NOT re-emit the parent row
  (the child Collection updates in place)
- toArray includes: child change DOES re-emit the parent row
  (the parent row is re-emitted with the updated array snapshot)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kevin-dp kevin-dp force-pushed the kevin/includes-aggregates branch from 939f886 to a6bad2a Compare March 12, 2026 14:12
@kevin-dp kevin-dp merged commit 6343522 into kevin/includes Mar 12, 2026
6 checks passed
@kevin-dp kevin-dp deleted the kevin/includes-aggregates branch March 12, 2026 14:19
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