Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

@KyleAMathews KyleAMathews commented Jan 26, 2026

Summary

Fixed $selected namespace availability in orderBy(), having(), and fn.having() clauses when using functional select (fn.select()). Users can now order and filter by computed/derived fields from functional selects.

Root Cause

The query builder only checked for this.query.select when deciding whether to expose the $selected namespace in orderBy and having callbacks. It did not account for this.query.fnSelect (functional select), so users couldn't reference $selected fields after using fn.select().

Approach

Added || this.query.fnSelect to the condition in both orderBy() and having() methods:

const refProxy = (
  this.query.select || this.query.fnSelect
    ? createRefProxyWithSelected(aliases)
    : createRefProxy(aliases)
) as RefsForContext<TContext>

Key Invariants

  • $selected is available in orderBy, having, and fn.having when either .select() or fn.select() has been called
  • Table refs remain accessible alongside $selected (both namespaces work together)
  • Ordering/filtering by original table fields still works after fn.select()

Limitations

  • fn.select + groupBy combination is not fully supported (returns only group keys, not computed fields)
  • For aggregates with groupBy, continue using regular .select() with .having() or .fn.having()

Verification

pnpm test:pr
pnpm vitest run packages/db/tests/query/functional-variants.test.ts

Files Changed

File Change
packages/db/src/query/builder/index.ts Added fnSelect check to orderBy and having methods
packages/db/tests/query/functional-variants.test.ts Added 7 new runtime tests for fn.select with orderBy and fn.having
packages/db/tests/query/functional-variants.test-d.ts Added 2 type tests for $selected type inference

https://claude.ai/code/session_01EPRCG2K8348FMNWzjPiacC

The orderBy method was only checking for regular select clause
(this.query.select) to determine if $selected should be exposed.
When using fn.select, the callback is stored in fnSelect instead,
so $selected was not available in orderBy callbacks.

This change updates orderBy to check for both select and fnSelect,
enabling queries like:
```ts
q.from({ user: usersCollection })
  .fn.select((row) => ({ name: row.user.name, salary: row.user.salary }))
  .orderBy(({ $selected }) => $selected.salary)
```

https://claude.ai/code/session_01EPRCG2K8348FMNWzjPiacC
@changeset-bot
Copy link

changeset-bot bot commented Jan 26, 2026

🦋 Changeset detected

Latest commit: 37725b2

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 Jan 26, 2026

More templates

@tanstack/angular-db

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/electric-db-collection

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

@tanstack/offline-transactions

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

@tanstack/powersync-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: 37725b2

@github-actions
Copy link
Contributor

github-actions bot commented Jan 26, 2026

Size Change: +9 B (+0.01%)

Total Size: 90.9 kB

Filename Size Change
./packages/db/dist/esm/query/builder/index.js 4.09 kB +9 B (+0.22%)
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.39 kB
./packages/db/dist/esm/collection/changes.js 1.19 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.68 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.62 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.7 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.69 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 1.93 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.1 kB
./packages/db/dist/esm/indexes/reverse-index.js 513 B
./packages/db/dist/esm/local-only.js 837 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 733 B
./packages/db/dist/esm/query/builder/ref-proxy.js 1.05 kB
./packages/db/dist/esm/query/compiler/evaluators.js 1.42 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/group-by.js 1.81 kB
./packages/db/dist/esm/query/compiler/index.js 2.02 kB
./packages/db/dist/esm/query/compiler/joins.js 2.07 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.45 kB
./packages/db/dist/esm/query/compiler/select.js 1.06 kB
./packages/db/dist/esm/query/expression-helpers.js 1.43 kB
./packages/db/dist/esm/query/ir.js 673 B
./packages/db/dist/esm/query/live-query-collection.js 360 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.42 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.93 kB
./packages/db/dist/esm/query/live/internal.js 145 B
./packages/db/dist/esm/query/optimizer.js 2.56 kB
./packages/db/dist/esm/query/predicate-utils.js 2.97 kB
./packages/db/dist/esm/query/subset-dedupe.js 921 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 852 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 Jan 26, 2026

Size Change: 0 B

Total Size: 3.7 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 225 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.17 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

Also adds tests for orderBy with table refs after fn.select.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The having method fix was made for consistency but couldn't be verified
with a passing test. Keeping only the orderBy fix which has comprehensive
test coverage.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@kevin-dp
Copy link
Contributor

kevin-dp commented Jan 27, 2026

@KyleAMathews The $selected namespace should also be available on having and fn.having clauses. You can point Claude to the original PR #1094 such that it has the necessary context to implement this fix also for having and fn.having (together with the necessary unit tests). It can copy paste the unit tests from the original PR and replace select by fn.select.

- Added fnSelect check to having method for $selected access
- Added test for fn.having with fn.select (no groupBy)
- Note: fn.select + groupBy combination requires further work

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@KyleAMathews
Copy link
Collaborator Author

Thanks for the feedback @kevin-dp!

I've added $selected support for having and fn.having when using fn.select. The fix follows the same pattern as orderBy - checking for fnSelect when deciding whether to create the ref proxy with $selected.

What works now:

  • fn.selectorderBy with $selected
  • fn.selectfn.having with $selected (no groupBy) ✅
  • fn.selecthaving with $selected (needs groupBy for regular having)

Limitation found:
When I tried to add tests that combine fn.select + groupBy + having/fn.having (as you suggested - replacing select with fn.select in the existing tests), I discovered that fn.select + groupBy doesn't work correctly. The query returns only { __key_0: ... } instead of the computed fields.

This happens because:

  1. fn.select runs before groupBy, creating $selected per-row
  2. processGroupBy() doesn't know about fnSelect (only receives selectClause)
  3. Without a selectClause, it falls back to returning only the group keys

Supporting fn.select + groupBy would require changes to the query compiler to either:

  • Pass fnSelect to processGroupBy() and preserve the first row's $selected values
  • Or extract aggregate expressions from fnSelect (not possible since it's a JS function, not an expression tree)

For now, users should continue using regular .select() for aggregates with groupBy, and can use fn.selectfn.having for non-grouped filtering use cases.

Let me know if you'd like me to pursue the fn.select + groupBy feature as a follow-up!

Copy link
Contributor

@kevin-dp kevin-dp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this and follow up on the fn.select + having in a separate follow up PR.

@KyleAMathews KyleAMathews merged commit 7099459 into main Jan 27, 2026
7 checks passed
@KyleAMathews KyleAMathews deleted the claude/fix-select-orderby-queries-lFXti branch January 27, 2026 16:17
@KyleAMathews
Copy link
Collaborator Author

Posted issue to #1189

@github-actions github-actions bot mentioned this pull request Jan 27, 2026
@KyleAMathews KyleAMathews moved this from Ready for review to Done in TanStack DB 1.0.0 release Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

4 participants