File tree Expand file tree Collapse file tree 3 files changed +50
-3
lines changed Expand file tree Collapse file tree 3 files changed +50
-3
lines changed Original file line number Diff line number Diff line change @@ -211,7 +211,7 @@ export class CatsController {
211211
212212### Config
213213
214- ``` ts
214+ ```` ts
215215const paginateConfig: PaginateConfig <CatEntity > {
216216 /**
217217 * Required: true (must have a minimum of one column)
@@ -376,8 +376,31 @@ const paginateConfig: PaginateConfig<CatEntity> {
376376 * will be treated as a separate search term, allowing for more flexible matching.
377377 */
378378 multiWordSearch : false ,
379+
380+ /**
381+ * Required: false
382+ * Type: (qb: SelectQueryBuilder<T>) => SelectQueryBuilder<any>
383+ * Default: undefined
384+ * Description: Callback that lets you override the COUNT query executed by
385+ * paginate(). The function receives a **clone** of the original QueryBuilder,
386+ * so it already contains every WHERE clause and parameter parsed by
387+ * nestjs-paginate.
388+ *
389+ * Typical use-case: remove expensive LEFT JOINs or build a lighter DISTINCT
390+ * count when getManyAndCount() becomes a bottleneck.
391+ *
392+ * Example:
393+ * ```ts
394+ * buildCountQuery: qb => {
395+ * qb.expressionMap.joinAttributes = []; // drop all joins
396+ * qb.select('p.id').distinct(true); // keep DISTINCT on primary key
397+ * return qb; // paginate() will call .getCount()
398+ * }
399+ * ```
400+ */
401+ buildCountQuery : (qb : SelectQueryBuilder <T >) => SelectQueryBuilder < any > ,
379402}
380- ```
403+ ````
381404
382405## Usage with Query Builder
383406
Original file line number Diff line number Diff line change @@ -2993,6 +2993,24 @@ describe('paginate', () => {
29932993 expect ( result . links . current ) . toBe ( '?page=1&limit=20&sortBy=id:ASC' )
29942994 } )
29952995
2996+ it ( 'uses custom count builder when provided' , async ( ) => {
2997+ const fakeQB = { getCount : jest . fn ( ) . mockResolvedValue ( 42 ) } as any
2998+ const config : PaginateConfig < CatEntity > = {
2999+ sortableColumns : [ 'id' ] ,
3000+ select : [ 'id' , 'name' , 'color' ] ,
3001+ buildCountQuery : ( ) => fakeQB ,
3002+ }
3003+ const query : PaginateQuery = {
3004+ path : '' ,
3005+ select : [ 'id' , 'color' ] ,
3006+ }
3007+
3008+ const page = await paginate < CatEntity > ( query , catRepo , config )
3009+
3010+ expect ( fakeQB . getCount ) . toHaveBeenCalledTimes ( 1 )
3011+ expect ( page . meta . totalItems ) . toBe ( 42 )
3012+ } )
3013+
29963014 describe ( 'should return result based on date column filter' , ( ) => {
29973015 it ( 'with $not and $null operators' , async ( ) => {
29983016 const config : PaginateConfig < CatEntity > = {
Original file line number Diff line number Diff line change @@ -90,6 +90,7 @@ export interface PaginateConfig<T> {
9090 multiWordSearch ?: boolean
9191 defaultJoinMethod ?: JoinMethod
9292 joinMethods ?: Partial < MappedColumns < T , JoinMethod > >
93+ buildCountQuery ?: ( qb : SelectQueryBuilder < T > ) => SelectQueryBuilder < any >
9394}
9495
9596export enum PaginationLimit {
@@ -819,7 +820,12 @@ export async function paginate<T extends ObjectLiteral>(
819820 if ( query . limit === PaginationLimit . COUNTER_ONLY ) {
820821 totalItems = await queryBuilder . getCount ( )
821822 } else if ( isPaginated && config . paginationType !== PaginationType . CURSOR ) {
822- ; [ items , totalItems ] = await queryBuilder . getManyAndCount ( )
823+ if ( config . buildCountQuery ) {
824+ items = await queryBuilder . getMany ( )
825+ totalItems = await config . buildCountQuery ( queryBuilder . clone ( ) ) . getCount ( )
826+ } else {
827+ ; [ items , totalItems ] = await queryBuilder . getManyAndCount ( )
828+ }
823829 } else {
824830 items = await queryBuilder . getMany ( )
825831 }
You can’t perform that action at this time.
0 commit comments