Skip to content

feat(core): add em.countBy() for grouped counting#7372

Merged
B4nan merged 4 commits intonextfrom
feat/count-by
Apr 18, 2026
Merged

feat(core): add em.countBy() for grouped counting#7372
B4nan merged 4 commits intonextfrom
feat/count-by

Conversation

@B4nan
Copy link
Copy Markdown
Member

@B4nan B4nan commented Mar 21, 2026

Summary

  • Adds em.countBy(entityName, groupBy, options?) — counts entities grouped by one or more properties, returning Dictionary<number>
  • Type-safe groupBy parameter (EntityKey<Entity> | readonly EntityKey<Entity>[])
  • For composite groupBy, result keys are joined with ~~~ (Utils.PK_SEPARATOR)
  • SQL: single GROUP BY query via QueryBuilder
  • MongoDB: aggregation pipeline with $group + $sum
  • Also available on EntityRepository via repo.countBy(groupBy, options?)
  • New CountByOptions type extends CountOptions (without groupBy) and adds where
const counts = await em.countBy(Book, 'author');
// { '1': 2, '2': 1, '3': 3 }

const counts = await em.countBy(Order, ['status', 'country']);
// { 'pending~~~US': 5, 'shipped~~~DE': 3 }

🤖 Generated with Claude Code

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.65%. Comparing base (b546277) to head (0711057).
⚠️ Report is 2 commits behind head on next.

Additional details and impacted files
@@           Coverage Diff            @@
##             next    #7372    +/-   ##
========================================
  Coverage   99.65%   99.65%            
========================================
  Files         263      263            
  Lines       26888    26942    +54     
  Branches     6985     7455   +470     
========================================
+ Hits        26794    26848    +54     
  Misses         88       88            
  Partials        6        6            

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@B4nan B4nan force-pushed the feat/count-by branch 2 times, most recently from 2dcf6fe to 3b6d014 Compare April 1, 2026 14:53
@B4nan B4nan force-pushed the feat/count-by branch 2 times, most recently from ad5be5b to cfbd345 Compare April 18, 2026 15:22
@B4nan B4nan changed the base branch from master to next April 18, 2026 15:22
B4nan and others added 4 commits April 18, 2026 17:43
Adds `em.countBy(entityName, groupBy, options?)` — counts entities
grouped by one or more properties, returning `Dictionary<number>`.
For composite `groupBy`, result keys are joined with `~~~`.

SQL drivers issue a single GROUP BY query via QueryBuilder. MongoDB
uses an aggregation pipeline with `$group` + `$sum`. Also available
on repositories via `repo.countBy(groupBy, options?)`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- SQL/MongoDB countBy now calls processWhere (applies entity filters and
  discriminator conditions) and tryFlush (honors FlushMode.AUTO/ALWAYS)
- MongoDB countBy routes through driver.aggregate() for transaction
  context, query logging, and error wrapping; applies renameFields to
  the where clause for correct field name mapping
- Trim CountByOptions to only relevant properties instead of inheriting
  all of CountOptions
- Fix unquoted SQL alias for MySQL compatibility
- Move SQL countBy tests to dedicated test file with filter coverage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Call prepareOptions() in both SQL and MongoDB countBy so EM-level
  schema defaults and logger context are inherited (matching every
  other EM method)
- Throw when `having` is passed to MongoDB countBy instead of
  silently ignoring it
- Add test for nullable groupBy columns (null publisher key)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exercise the remaining branches of the new `countBy` API so the PR
reaches 100% patch coverage on the touched source files:

- base `EntityManager.countBy` throw for drivers that don't override it
- SQL `having`/`schema` option paths
- SQL/Mongo raw-field fallback when groupBy isn't a declared property
- Mongo composite groupBy pipeline
- Mongo `having` rejection
@B4nan B4nan merged commit 802fe72 into next Apr 18, 2026
20 checks passed
@B4nan B4nan deleted the feat/count-by branch April 18, 2026 16:19
B4nan added a commit that referenced this pull request Apr 18, 2026
## Summary

- Adds dataloader batching for `Collection.loadCount()` — multiple count
calls in the same tick are grouped into a single query
- 1:M relations: uses `em.countBy()` for a single GROUP BY query
- M:N relations: falls back to parallel `em.count()` calls (entity
filters correctly applied)
- Adds `dataloader` option to `LoadCountOptions`, respects global
`DataloaderType.ALL`/`COLLECTION` config

Depends on #7372.

Closes #6425

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B4nan added a commit that referenced this pull request Apr 19, 2026
## Summary

- Adds `em.countBy(entityName, groupBy, options?)` — counts entities
grouped by one or more properties, returning `Dictionary<number>`
- Type-safe `groupBy` parameter (`EntityKey<Entity> | readonly
EntityKey<Entity>[]`)
- For composite `groupBy`, result keys are joined with `~~~`
(`Utils.PK_SEPARATOR`)
- SQL: single `GROUP BY` query via QueryBuilder
- MongoDB: aggregation pipeline with `$group` + `$sum`
- Also available on `EntityRepository` via `repo.countBy(groupBy,
options?)`
- New `CountByOptions` type extends `CountOptions` (without `groupBy`)
and adds `where`

```ts
const counts = await em.countBy(Book, 'author');
// { '1': 2, '2': 1, '3': 3 }

const counts = await em.countBy(Order, ['status', 'country']);
// { 'pending~~~US': 5, 'shipped~~~DE': 3 }
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B4nan added a commit that referenced this pull request Apr 19, 2026
## Summary

- Adds dataloader batching for `Collection.loadCount()` — multiple count
calls in the same tick are grouped into a single query
- 1:M relations: uses `em.countBy()` for a single GROUP BY query
- M:N relations: falls back to parallel `em.count()` calls (entity
filters correctly applied)
- Adds `dataloader` option to `LoadCountOptions`, respects global
`DataloaderType.ALL`/`COLLECTION` config

Depends on #7372.

Closes #6425

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B4nan added a commit that referenced this pull request Apr 20, 2026
## Summary

- Adds `em.countBy(entityName, groupBy, options?)` — counts entities
grouped by one or more properties, returning `Dictionary<number>`
- Type-safe `groupBy` parameter (`EntityKey<Entity> | readonly
EntityKey<Entity>[]`)
- For composite `groupBy`, result keys are joined with `~~~`
(`Utils.PK_SEPARATOR`)
- SQL: single `GROUP BY` query via QueryBuilder
- MongoDB: aggregation pipeline with `$group` + `$sum`
- Also available on `EntityRepository` via `repo.countBy(groupBy,
options?)`
- New `CountByOptions` type extends `CountOptions` (without `groupBy`)
and adds `where`

```ts
const counts = await em.countBy(Book, 'author');
// { '1': 2, '2': 1, '3': 3 }

const counts = await em.countBy(Order, ['status', 'country']);
// { 'pending~~~US': 5, 'shipped~~~DE': 3 }
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
B4nan added a commit that referenced this pull request Apr 20, 2026
## Summary

- Adds dataloader batching for `Collection.loadCount()` — multiple count
calls in the same tick are grouped into a single query
- 1:M relations: uses `em.countBy()` for a single GROUP BY query
- M:N relations: falls back to parallel `em.count()` calls (entity
filters correctly applied)
- Adds `dataloader` option to `LoadCountOptions`, respects global
`DataloaderType.ALL`/`COLLECTION` config

Depends on #7372.

Closes #6425

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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