Skip to content

feat: better faceting performance#6369

Merged
KevinVandy merged 1 commit into
betafrom
perf-faceting
Jul 3, 2026
Merged

feat: better faceting performance#6369
KevinVandy merged 1 commit into
betafrom
perf-faceting

Conversation

@KevinVandy

@KevinVandy KevinVandy commented Jul 3, 2026

Copy link
Copy Markdown
Member

🎯 Changes

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

Summary by CodeRabbit

  • New Features

    • Improved faceting behavior so empty facet sets no longer trigger unnecessary work, helping tables stay responsive.
    • Facet calculations now reuse cached results more effectively, reducing repeated processing during filtering.
  • Bug Fixes

    • Faceted counts now handle empty values more reliably.
    • Filtering behavior is more consistent when combining multiple column filters.
  • Chores

    • Updated performance tracking notes and task configuration to include the latest scripts and test targets.

@KevinVandy KevinVandy requested a review from a team as a code owner July 3, 2026 01:44
@nx-cloud

nx-cloud Bot commented Jul 3, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 09543d4

Command Status Duration Result
nx affected --targets=test:eslint,test:sherif,t... ✅ Succeeded 6m 16s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 46s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-03 01:51:27 UTC

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Refactors table-core faceted row model caching to use per-table memoized factory functions keyed by column ID instead of re-resolving factories per call, adds an early-return optimization for empty filterable columns, simplifies unique-value counting, adds unit tests, and updates perf tracking docs and build target dependencies.

Changes

Faceted row model performance changes

Layer / File(s) Summary
Faceted cache type contracts
packages/table-core/src/features/column-faceting/columnFacetingFeature.types.ts
CachedRowModel_Faceted switches from per-column function signatures to Record<string, ...> keyed factories for faceted row model, min/max, and unique-value caches, plus global variants.
Per-table faceted factory memoization
packages/table-core/src/features/column-faceting/columnFacetingFeature.utils.ts
Per-column and global faceted row model/min-max/unique-values factories are lazily resolved once and memoized in table._rowModels instead of being re-derived from table.options.features on every call.
Empty filterableIds early return
packages/table-core/src/features/column-faceting/createFacetedRowModel.ts
_createFacetedRowModel returns preRowModel immediately when filterableIds is empty, skipping unnecessary filtering.
Unique value counting refactor
packages/table-core/src/features/column-faceting/createFacetedUniqueValues.ts
_createFacetedUniqueValues counts values via a single get/set instead of a has-guarded conditional.
Column faceting unit tests
packages/table-core/tests/unit/features/column-faceting/columnFacetingFeature.test.ts
New test suite validates row-model reuse, cross-column filter interaction, undefined value counting, and per-column/global factory caching semantics.
Perf docs and build configuration updates
perf-done.md, perf-todo.md, nx.json, package.json
Perf catalog entries/totals are updated to reflect the faceting optimizations, and nx.json/package.json add build-target dependsOn entries and a format included script.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Column
  participant TableRowModels as table._rowModels
  participant Factory as Faceted Factory

  Column->>TableRowModels: column_getFacetedRowModel(columnId)
  alt cache miss
    TableRowModels->>Factory: resolve factory for columnId
    Factory-->>TableRowModels: store factory
  end
  TableRowModels-->>Column: return cached factory result
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: a performance-focused update to faceting behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf-faceting

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Jul 3, 2026

Copy link
Copy Markdown
More templates

@tanstack/alpine-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/alpine-table@6369

@tanstack/angular-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/angular-table@6369

@tanstack/angular-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/angular-table-devtools@6369

@tanstack/lit-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/lit-table@6369

@tanstack/match-sorter-utils

npm i https://pkg.pr.new/TanStack/table/@tanstack/match-sorter-utils@6369

@tanstack/preact-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/preact-table@6369

@tanstack/preact-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/preact-table-devtools@6369

@tanstack/react-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/react-table@6369

@tanstack/react-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/react-table-devtools@6369

@tanstack/solid-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/solid-table@6369

@tanstack/solid-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/solid-table-devtools@6369

@tanstack/svelte-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/svelte-table@6369

@tanstack/table-core

npm i https://pkg.pr.new/TanStack/table/@tanstack/table-core@6369

@tanstack/table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/table-devtools@6369

@tanstack/vue-table

npm i https://pkg.pr.new/TanStack/table/@tanstack/vue-table@6369

@tanstack/vue-table-devtools

npm i https://pkg.pr.new/TanStack/table/@tanstack/vue-table-devtools@6369

commit: 09543d4

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/table-core/tests/unit/features/column-faceting/columnFacetingFeature.test.ts (1)

104-151: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider strengthening the caching test's assertions.

facetedMinMaxValues and facetedUniqueValues mocks are only checked for call count (Lines 149-150), unlike facetedRowModel which also verifies call arguments via toHaveBeenNthCalledWith (Lines 147-148). Also, no test in this suite exercises the real createFacetedMinMaxValues() implementation end-to-end (the row-model and unique-values wrappers each get a real integration test, but min/max only appears here as a mock used solely to validate caching).

♻️ Suggested additions
     expect(facetedMinMaxValues).toHaveBeenCalledTimes(2)
+    expect(facetedMinMaxValues).toHaveBeenNthCalledWith(1, table, 'status')
+    expect(facetedMinMaxValues).toHaveBeenNthCalledWith(2, table, '__global__')
     expect(facetedUniqueValues).toHaveBeenCalledTimes(2)
+    expect(facetedUniqueValues).toHaveBeenNthCalledWith(1, table, 'status')
+    expect(facetedUniqueValues).toHaveBeenNthCalledWith(2, table, '__global__')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/table-core/tests/unit/features/column-faceting/columnFacetingFeature.test.ts`
around lines 104 - 151, Strengthen the caching test in
columnFacetingFeature.test by asserting the actual arguments passed to
facetedMinMaxValues and facetedUniqueValues, similar to the existing
toHaveBeenNthCalledWith checks on facetedRowModel, so the cache key behavior is
fully verified. Also add a real end-to-end test for createFacetedMinMaxValues
like the existing integration coverage for the row-model and unique-values
faceting wrappers, using the relevant column faceting helpers to ensure the
implementation works beyond the mock-only caching case.
packages/table-core/src/features/column-faceting/columnFacetingFeature.utils.ts (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Six near-identical caching blocks — extract a shared helper.

The per-column (Record-keyed) and global (single-field) caching logic is duplicated three times each with only the feature-fn name/fallback differing. Worth consolidating into one or two small generic helpers to reduce duplication and future drift risk (e.g. if the caching strategy needs to change later, it must be updated in 6 places).

♻️ Example consolidation
+function getOrCreateFacetFactory<TFn>(
+  cache: Record<string, TFn>,
+  key: string,
+  resolve: () => TFn | undefined,
+  fallback: TFn,
+): TFn {
+  let fn = cache[key]
+  if (!fn) {
+    fn = cache[key] = resolve() ?? fallback
+  }
+  return fn
+}
+
 export function column_getFacetedMinMaxValues<...>(...): [number, number] | undefined {
   const facetedMinMaxValues = (table._rowModels.facetedMinMaxValues ??= makeObjectMap())
-  let facetedMinMaxValuesFn = facetedMinMaxValues[column.id]
-
-  if (!facetedMinMaxValuesFn) {
-    facetedMinMaxValuesFn = facetedMinMaxValues[column.id] =
-      table.options.features.facetedMinMaxValues?.(table, column.id) ??
-      (() => undefined)
-  }
-
-  return facetedMinMaxValuesFn()
+  return getOrCreateFacetFactory(
+    facetedMinMaxValues,
+    column.id,
+    () => table.options.features.facetedMinMaxValues?.(table, column.id),
+    () => undefined,
+  )()
 }

A similar single-slot variant can wrap the three global getters.

Also applies to: 27-36, 60-70, 93-102, 121-127, 147-153, 172-178

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/table-core/src/features/column-faceting/columnFacetingFeature.utils.ts`
at line 1, The column faceting utilities contain repeated caching logic across
the per-column and global getters, so extract a shared helper to centralize the
memoization behavior. Update the functions in columnFacetingFeature.utils.ts
that currently repeat the same cache-check/store pattern, using makeObjectMap
and the existing feature getter names/fallbacks as inputs to a generic helper.
Keep the helper flexible enough to cover both the Record-keyed column cache and
the single-slot global cache, then wire the existing getters to it so the
feature-fn-specific differences remain the only per-call variation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@packages/table-core/src/features/column-faceting/columnFacetingFeature.utils.ts`:
- Line 1: The column faceting utilities contain repeated caching logic across
the per-column and global getters, so extract a shared helper to centralize the
memoization behavior. Update the functions in columnFacetingFeature.utils.ts
that currently repeat the same cache-check/store pattern, using makeObjectMap
and the existing feature getter names/fallbacks as inputs to a generic helper.
Keep the helper flexible enough to cover both the Record-keyed column cache and
the single-slot global cache, then wire the existing getters to it so the
feature-fn-specific differences remain the only per-call variation.

In
`@packages/table-core/tests/unit/features/column-faceting/columnFacetingFeature.test.ts`:
- Around line 104-151: Strengthen the caching test in columnFacetingFeature.test
by asserting the actual arguments passed to facetedMinMaxValues and
facetedUniqueValues, similar to the existing toHaveBeenNthCalledWith checks on
facetedRowModel, so the cache key behavior is fully verified. Also add a real
end-to-end test for createFacetedMinMaxValues like the existing integration
coverage for the row-model and unique-values faceting wrappers, using the
relevant column faceting helpers to ensure the implementation works beyond the
mock-only caching case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 809e35be-fdf1-4048-96be-b35a6b524cb7

📥 Commits

Reviewing files that changed from the base of the PR and between 82cf612 and 09543d4.

📒 Files selected for processing (9)
  • nx.json
  • package.json
  • packages/table-core/src/features/column-faceting/columnFacetingFeature.types.ts
  • packages/table-core/src/features/column-faceting/columnFacetingFeature.utils.ts
  • packages/table-core/src/features/column-faceting/createFacetedRowModel.ts
  • packages/table-core/src/features/column-faceting/createFacetedUniqueValues.ts
  • packages/table-core/tests/unit/features/column-faceting/columnFacetingFeature.test.ts
  • perf-done.md
  • perf-todo.md

@KevinVandy KevinVandy merged commit 2bda964 into beta Jul 3, 2026
8 checks passed
@KevinVandy KevinVandy deleted the perf-faceting branch July 3, 2026 01:53
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.

1 participant