Skip to content

Commit

Permalink
slim count (#949)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: 

Limit = 0 (a single count query is generated)
Limit = -1 (act like old 0 unlimited result)
  • Loading branch information
xMase committed Jul 11, 2024
1 parent 5a787dd commit f236c80
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"runtimeArgs": [
"--preserve-symlinks",
"run",
"test",
"test:watch",
"--",
"--inspect-brk",
],
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ const paginateConfig: PaginateConfig<CatEntity> {
* Type: number
* Default: 100
* Description: The maximum amount of entities to return per page.
* Set it to 0, in conjunction with limit=0 on query param, to disable pagination.
* Set it to -1, in conjunction with limit=-1 on query param, to disable pagination.
*/
maxLimit: 20,

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"format:ci": "prettier --list-different \"src/**/*.ts\"",
"lint": "eslint -c .eslintrc.json --ext .ts --max-warnings 0 src",
"test": "jest",
"test:watch": "jest --watch",
"test:watch": "jest --watch ",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"
},
Expand Down
78 changes: 69 additions & 9 deletions src/paginate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
isSuffix,
parseFilterToken,
} from './filter'
import { NO_PAGINATION, PaginateConfig, Paginated, paginate } from './paginate'
import { PaginateConfig, Paginated, PaginationLimit, paginate } from './paginate'

const isoStringToDate = (isoString) => new Date(isoString)

Expand Down Expand Up @@ -317,7 +317,7 @@ describe('paginate', () => {
}
const query: PaginateQuery = {
path: '',
limit: NO_PAGINATION,
limit: PaginationLimit.NO_PAGINATION,
}

const result = await paginate<CatEntity>(query, catRepo, config)
Expand All @@ -327,39 +327,39 @@ describe('paginate', () => {
it('should return all cats', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
maxLimit: NO_PAGINATION,
maxLimit: PaginationLimit.NO_PAGINATION,
defaultLimit: 1,
}
const query: PaginateQuery = {
path: '',
limit: NO_PAGINATION,
limit: PaginationLimit.NO_PAGINATION,
}

const result = await paginate<CatEntity>(query, catRepo, config)

expect(result.data).toStrictEqual(cats)
})

it('should limit to defaultLimit, if limit is negative', async () => {
it('should limit to defaultLimit, if limit is differt FROM NO_PAGINATION ecc....', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
maxLimit: NO_PAGINATION,
maxLimit: PaginationLimit.NO_PAGINATION,
defaultLimit: 1,
}
const query: PaginateQuery = {
path: '',
limit: -1,
limit: -2,
}

const result = await paginate<CatEntity>(query, catRepo, config)

expect(result.data).toStrictEqual(cats.slice(0, 1))
})

it('should default to limit defaultLimit, if maxLimit is 0', async () => {
it('should default to limit defaultLimit, if maxLimit is NO_PAGINATION', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
maxLimit: NO_PAGINATION,
maxLimit: PaginationLimit.NO_PAGINATION,
defaultLimit: 1,
}
const query: PaginateQuery = {
Expand Down Expand Up @@ -404,6 +404,66 @@ describe('paginate', () => {
expect(result.data).toStrictEqual(cats.slice(0, 2))
})

it('maxLimit should limit defaultLimit', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
maxLimit: 1,
defaultLimit: 2,
}
const query: PaginateQuery = {
path: '',
}

const result = await paginate<CatEntity>(query, catRepo, config)

expect(result.data).toStrictEqual(cats.slice(0, 1))
})

it('limit should bypass defaultLimit', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
defaultLimit: 1,
}
const query: PaginateQuery = {
path: '',
limit: 2,
}

const result = await paginate<CatEntity>(query, catRepo, config)

expect(result.data).toStrictEqual(cats.slice(0, 2))
})

it('DEFAULT_LIMIT should be used as the limit if limit is set to NO_PAGINATION and maxLimit is not specified.', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
}
const query: PaginateQuery = {
path: '',
limit: PaginationLimit.NO_PAGINATION,
}

const result = await paginate<CatEntity>(query, catRepo, config)

expect(result.data).toStrictEqual(cats.slice(0, PaginationLimit.DEFAULT_LIMIT))
})

it('should return the count without data ignoring maxLimit if limit is COUNTER_ONLY', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
maxLimit: PaginationLimit.NO_PAGINATION,
}
const query: PaginateQuery = {
path: '',
limit: 0,
}

const result = await paginate<CatEntity>(query, catRepo, config)

expect(result.data).toStrictEqual([])
expect(result.meta.totalItems).toBe(5)
})

it('should return correct result for limited one-to-many relations', async () => {
const config: PaginateConfig<CatEntity> = {
relations: ['toys'],
Expand Down
36 changes: 25 additions & 11 deletions src/paginate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,12 @@ export interface PaginateConfig<T> {
ignoreSelectInQueryParam?: boolean
}

export const DEFAULT_MAX_LIMIT = 100
export const DEFAULT_LIMIT = 20
export const NO_PAGINATION = 0
export enum PaginationLimit {
NO_PAGINATION = -1,
COUNTER_ONLY = 0,
DEFAULT_LIMIT = 20,
DEFAULT_MAX_LIMIT = 100,
}

function generateWhereStatement<T>(
queryBuilder: SelectQueryBuilder<T>,
Expand Down Expand Up @@ -183,13 +186,22 @@ export async function paginate<T extends ObjectLiteral>(
): Promise<Paginated<T>> {
const page = positiveNumberOrDefault(query.page, 1, 1)

const defaultLimit = config.defaultLimit || DEFAULT_LIMIT
const maxLimit = positiveNumberOrDefault(config.maxLimit, DEFAULT_MAX_LIMIT)
const queryLimit = positiveNumberOrDefault(query.limit, defaultLimit)
const defaultLimit = config.defaultLimit || PaginationLimit.DEFAULT_LIMIT
const maxLimit = config.maxLimit || PaginationLimit.DEFAULT_MAX_LIMIT

const isPaginated = !(queryLimit === NO_PAGINATION && maxLimit === NO_PAGINATION)
const isPaginated = !(
query.limit === PaginationLimit.COUNTER_ONLY ||
(query.limit === PaginationLimit.NO_PAGINATION && maxLimit === PaginationLimit.NO_PAGINATION)
)

const limit = isPaginated ? Math.min(queryLimit || defaultLimit, maxLimit || DEFAULT_MAX_LIMIT) : NO_PAGINATION
const limit =
query.limit === PaginationLimit.COUNTER_ONLY
? PaginationLimit.COUNTER_ONLY
: isPaginated
? query.limit === PaginationLimit.NO_PAGINATION || maxLimit === PaginationLimit.NO_PAGINATION
? defaultLimit
: Math.min(query.limit ?? defaultLimit, maxLimit)
: defaultLimit

const sortBy = [] as SortBy<T>
const searchBy: Column<T>[] = []
Expand Down Expand Up @@ -372,7 +384,9 @@ export async function paginate<T extends ObjectLiteral>(
addFilter(queryBuilder, query, config.filterableColumns)
}

if (isPaginated) {
if (query.limit === PaginationLimit.COUNTER_ONLY) {
totalItems = await queryBuilder.getCount()
} else if (isPaginated) {
;[items, totalItems] = await queryBuilder.getManyAndCount()
} else {
items = await queryBuilder.getMany()
Expand Down Expand Up @@ -419,8 +433,8 @@ export async function paginate<T extends ObjectLiteral>(
const results: Paginated<T> = {
data: items,
meta: {
itemsPerPage: isPaginated ? limit : items.length,
totalItems: isPaginated ? totalItems : items.length,
itemsPerPage: limit === PaginationLimit.COUNTER_ONLY ? totalItems : isPaginated ? limit : items.length,
totalItems: limit === PaginationLimit.COUNTER_ONLY || isPaginated ? totalItems : items.length,
currentPage: page,
totalPages,
sortBy,
Expand Down
8 changes: 4 additions & 4 deletions src/swagger/api-paginated-query.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, FilterOperator, FilterSuffix, PaginateConfig } from '../paginate'
import { applyDecorators } from '@nestjs/common'
import { ApiQuery } from '@nestjs/swagger'
import { FilterComparator } from '../filter'
import { applyDecorators } from '@nestjs/common'
import { FilterOperator, FilterSuffix, PaginateConfig, PaginationLimit } from '../paginate'

const DEFAULT_VALUE_KEY = 'Default Value'

Expand Down Expand Up @@ -45,8 +45,8 @@ function Limit(paginationConfig: PaginateConfig<any>) {
name: 'limit',
description: `Number of records per page.
${p('Example', '20')}
${p(DEFAULT_VALUE_KEY, paginationConfig?.defaultLimit?.toString() || DEFAULT_LIMIT.toString())}
${p('Max Value', paginationConfig.maxLimit?.toString() || DEFAULT_MAX_LIMIT.toString())}
${p(DEFAULT_VALUE_KEY, paginationConfig?.defaultLimit?.toString() || PaginationLimit.DEFAULT_LIMIT.toString())}
${p('Max Value', paginationConfig.maxLimit?.toString() || PaginationLimit.DEFAULT_MAX_LIMIT.toString())}
If provided value is greater than max value, max value will be applied.
`,
Expand Down

0 comments on commit f236c80

Please sign in to comment.