Skip to content

enable live queries to use indexes on collections #258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: index-collections
Choose a base branch
from

Conversation

samwillis
Copy link
Collaborator

@samwillis samwillis commented Jul 13, 2025

stacked on #257 and #256

This connects the optimised queries with the collections indexes, pushing where clauses that only touch a single collection to the subscription. The subscription then handles using the indexes if they are available.

Note that the design of this means that the query optimiser and compiler does not need to have knowledge of indexes, any where clause that only touches a single collection is pushed to the subscription rather than handled as a filter int he D2mini pipeline.

Copy link

changeset-bot bot commented Jul 13, 2025

🦋 Changeset detected

Latest commit: 9ee908c

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

This PR includes changesets to release 5 packages
Name Type
@tanstack/db Patch
@tanstack/db-collections Patch
@tanstack/react-db Patch
@tanstack/vue-db Patch
@tanstack/db-example-react-todo 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

Copy link

pkg-pr-new bot commented Jul 13, 2025

@tanstack/db-example-react-todo

npm i https://pkg.pr.new/@tanstack/db@258
npm i https://pkg.pr.new/@tanstack/db-collections@258
npm i https://pkg.pr.new/@tanstack/react-db@258
npm i https://pkg.pr.new/@tanstack/vue-db@258

commit: 9ee908c

Copy link
Contributor

github-actions bot commented Jul 13, 2025

Size Change: +8.47 kB (+26.92%) 🚨

Total Size: 39.9 kB

Filename Size Change
./packages/db/dist/esm/collection.js 11.9 kB +3.58 kB (+43.33%) 🚨
./packages/db/dist/esm/query/builder/ref-proxy.js 890 B +48 B (+5.7%) 🔍
./packages/db/dist/esm/query/compiler/evaluators.js 1.46 kB +119 B (+8.91%) 🔍
./packages/db/dist/esm/query/compiler/index.js 1.76 kB +342 B (+24.12%) 🚨
./packages/db/dist/esm/query/compiler/joins.js 1.21 kB +64 B (+5.59%) 🔍
./packages/db/dist/esm/query/compiler/order-by.js 713 B -220 B (-23.58%) 🎉
./packages/db/dist/esm/query/live-query-collection.js 2.45 kB +392 B (+19.07%) ⚠️
./packages/db/dist/esm/query/compiler/expressions.js 646 B +646 B (new file) 🆕
./packages/db/dist/esm/query/optimizer.js 2.47 kB +2.47 kB (new file) 🆕
./packages/db/dist/esm/utils.js 423 B +423 B (new file) 🆕
./packages/db/dist/esm/utils/comparison.js 601 B +601 B (new file) 🆕
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/deferred.js 230 B
./packages/db/dist/esm/errors.js 150 B
./packages/db/dist/esm/index.js 528 B
./packages/db/dist/esm/optimistic-action.js 294 B
./packages/db/dist/esm/proxy.js 3.75 kB
./packages/db/dist/esm/query/builder/functions.js 531 B
./packages/db/dist/esm/query/builder/index.js 3.4 kB
./packages/db/dist/esm/query/compiler/group-by.js 2.09 kB
./packages/db/dist/esm/query/compiler/select.js 657 B
./packages/db/dist/esm/query/ir.js 318 B
./packages/db/dist/esm/SortedMap.js 1.24 kB
./packages/db/dist/esm/transactions.js 2.29 kB

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

Copy link
Contributor

github-actions bot commented Jul 13, 2025

Size Change: 0 B

Total Size: 1.05 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 152 B
./packages/react-db/dist/esm/useLiveQuery.js 902 B

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

@samwillis samwillis changed the base branch from main to index-collections July 13, 2025 14:22
@samwillis samwillis requested a review from KyleAMathews July 13, 2025 14:24
@samwillis
Copy link
Collaborator Author

We need to add tests that validate what happens when a row that was filtered out/in based on an index value during inital run, then changes to be filtered in/out. I have a feeling there may be a bug where we need to do a little book keeping on the subscribe to know when rows were sent, and then know if it's an update/insert or deleted/update on the pipeline.

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.

LGTM. I left a few small comments.

@@ -2459,7 +2478,7 @@ export class CollectionImpl<
const operation = expression.name as `eq` | `gt` | `gte` | `lt` | `lte`

// Find an index that matches this field
for (const index of this.indexes.values()) {
for (const [, index] of this.indexes) {
Copy link
Contributor

Choose a reason for hiding this comment

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

i much preferred how it was before

}
}

return checkExpression(whereClause)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why would we define an inner function for this?
I'm not sure i'm a find of the switch statement, this is equivalent:

const tpe = expr.type
if (tpe === `func`) {
  // Check if this function is supported
  if (!SUPPORTED_COLLECTION_FUNCS.has(expr.name)) {
    return false
  }
  // Recursively check all arguments
  return expr.args.every((arg) =>
    checkExpression(arg as BasicExpression<boolean>)
  )
}
return [`val`, `ref`].includes(tpe)

* @param collectionAlias - The alias of the collection being filtered
* @returns The converted BasicExpression or null if conversion fails
*/
export function convertToBasicExpression(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comments on this function. No need for an inner function + i'd prefer an if statement instead of a switch.

query,
inputsCache as Record<string, KeyedStream>
)

pipelineCache = compilationResult.pipeline
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not destructuring it? :-)

const aIsObject = typeof a === `object` && a !== null
const bIsObject = typeof b === `object` && b !== null

if (aIsObject || bIsObject) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Checking for null is actually not needed because at the very beginning of this function we already check for null and if a or b is null we immediately return.

return 0
} catch {
// If comparison fails, fall back to string comparison
return safeStringify(a).localeCompare(safeStringify(b))
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think comparison will ever throw on primitive values?

@samwillis samwillis force-pushed the index-collections branch from 95b29d8 to e526c1e Compare July 16, 2025 11:16
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