Skip to content

Conversation

samwillis
Copy link
Collaborator

This removes all the additional state that was needed for each of the composed joins, and the need for the consolidate step. Delivers a father 50% speedup over the index refactor PR #549 this is stacked on.

Copy link

changeset-bot bot commented Sep 19, 2025

🦋 Changeset detected

Latest commit: d7d8b48

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

This PR includes changesets to release 13 packages
Name Type
@tanstack/db-ivm Patch
@tanstack/db Patch
@tanstack/angular-db Patch
@tanstack/electric-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
todos 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 Sep 19, 2025

More templates

@tanstack/angular-db

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

@tanstack/db

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

@tanstack/db-ivm

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

@tanstack/electric-db-collection

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

@tanstack/query-db-collection

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

@tanstack/react-db

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

@tanstack/rxdb-db-collection

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

@tanstack/solid-db

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

@tanstack/svelte-db

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

@tanstack/trailbase-db-collection

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

@tanstack/vue-db

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

commit: d7d8b48

Copy link
Contributor

github-actions bot commented Sep 19, 2025

Size Change: -14 B (-0.02%)

Total Size: 66.6 kB

Filename Size Change
./packages/db/dist/esm/query/compiler/joins.js 2.5 kB -14 B (-0.56%)
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/change-events.js 1.13 kB
./packages/db/dist/esm/collection.js 10.5 kB
./packages/db/dist/esm/deferred.js 230 B
./packages/db/dist/esm/errors.js 3.1 kB
./packages/db/dist/esm/index.js 1.55 kB
./packages/db/dist/esm/indexes/auto-index.js 745 B
./packages/db/dist/esm/indexes/base-index.js 605 B
./packages/db/dist/esm/indexes/btree-index.js 1.74 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.25 kB
./packages/db/dist/esm/local-only.js 827 B
./packages/db/dist/esm/local-storage.js 2.03 kB
./packages/db/dist/esm/optimistic-action.js 294 B
./packages/db/dist/esm/proxy.js 3.87 kB
./packages/db/dist/esm/query/builder/functions.js 615 B
./packages/db/dist/esm/query/builder/index.js 3.93 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 938 B
./packages/db/dist/esm/query/compiler/evaluators.js 1.52 kB
./packages/db/dist/esm/query/compiler/expressions.js 631 B
./packages/db/dist/esm/query/compiler/group-by.js 2.08 kB
./packages/db/dist/esm/query/compiler/index.js 2.27 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.23 kB
./packages/db/dist/esm/query/compiler/select.js 1.28 kB
./packages/db/dist/esm/query/ir.js 508 B
./packages/db/dist/esm/query/live-query-collection.js 333 B
./packages/db/dist/esm/query/live/collection-config-builder.js 2.59 kB
./packages/db/dist/esm/query/live/collection-subscriber.js 2.4 kB
./packages/db/dist/esm/query/optimizer.js 3.05 kB
./packages/db/dist/esm/SortedMap.js 1.24 kB
./packages/db/dist/esm/transactions.js 2.29 kB
./packages/db/dist/esm/utils.js 943 B
./packages/db/dist/esm/utils/btree.js 6.02 kB
./packages/db/dist/esm/utils/comparison.js 718 B
./packages/db/dist/esm/utils/index-optimization.js 1.62 kB

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

Copy link
Contributor

github-actions bot commented Sep 19, 2025

Size Change: 0 B

Total Size: 1.18 kB

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

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

* - `indexB: Index<K, V2>` - all right-side rows accumulated over time
*
* **Mass maps** track presence efficiently:
* - `massA/massB: Map<K, number>` - sum of multiplicities per key
Copy link
Contributor

Choose a reason for hiding this comment

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

Essentially, this map keeps a consolidated multiset. I had a version of the Index that internally keeps such a map instead of an array. I think it makes more sense to modify the index to keep a consolidated map, rather than having the index and then also keeping a map here. This is a concern of the index.

* Operator that joins two input streams
* Helper to build delta index and mass map from messages
*/
function buildDelta<K, V>(
Copy link
Contributor

Choose a reason for hiding this comment

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

This is definitely an index concern and not a join concern. The index should keep the "mass" (btw i think this is a very bad name, it's just (consolidated) multiplicity) and the join operator should simply do index.addValue(key, [value, multiplicity]) when it's going through the messages. No need for buildDelta.


// Precompute mode flags to avoid repeated string comparisons
const mode = this.#mode
const emitInner =
Copy link
Contributor

Choose a reason for hiding this comment

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

isn't emitInner always true?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, no it's false when we're doing an anti join. Perhaps const emitInner = mode !== 'anti' is more accurate.

if (multiplicity !== 0) {
results.add(
[key, [value, null]],
retract ? -multiplicity : +multiplicity
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 get the retract logic. If the key used to have multiplicity 0, then retract is true so we will add the new change to the index but with its negated multiplicity, why?

const after = before + deltaMass

// Skip if presence doesn't flip (0->0, >0->different>0)
if ((before === 0) === (after === 0)) continue
Copy link
Contributor

Choose a reason for hiding this comment

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

Even if the presence doesn't flip, don't we need to update the indexes/mass index ?
Say, that before is 5 and the new delta has multiplicity -3. Then after is 2. So it is still visible and this won't update the index so in the index the key will still have multiplicity 5. Next time, if it receives a multiplicity of -3 the key will still not flip (even though it should at this point!) because it thinks the multiplicity is 5. This looks wrong to me?

this.#mode = mode
}

run(): void {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would introduce helper functions to simplify this method and that way we could also unit test the helper functions in isolation. This method contains 4 steps, each step could be extracted to a helper function.

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