fix: make take/skip default (#573)

This commit is contained in:
Philipp 2023-04-02 21:50:28 +02:00 committed by GitHub
parent 1e7e67533e
commit 878e34ad5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 179 additions and 162 deletions

View File

@ -260,14 +260,11 @@ const paginateConfig: PaginateConfig<CatEntity> {
* Required: false
* Type: string
* Description: Allow user to choose between limit/offset and take/skip.
* Default: PaginationType.LIMIT_AND_OFFSET
* Default: PaginationType.TAKE_AND_SKIP
*
* However, using take/skip can cause problems with sorting and selections.
* For more information see:
* [#4742](https://github.com/typeorm/typeorm/issues/4742)
* [#5670](https://github.com/typeorm/typeorm/issues/5670)
* However, using limit/offset can cause problems with relations.
*/
paginationType: PaginationType.TAKE_AND_SKIP,
paginationType: PaginationType.LIMIT_AND_OFFSET,
/**
* Required: false

View File

@ -319,6 +319,22 @@ describe('paginate', () => {
expect(result.data).toStrictEqual(cats.slice(0, 2))
})
it('should return correct result for limited one-to-many relations', async () => {
const config: PaginateConfig<CatEntity> = {
relations: ['toys'],
sortableColumns: ['id', 'toys.id'],
searchableColumns: ['name', 'toys.name'],
defaultLimit: 4,
}
const query: PaginateQuery = {
path: '',
}
const result = await paginate<CatEntity>(query, catRepo, config)
expect(result.data.length).toStrictEqual(4)
})
it('should return correct links for some results', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
@ -1828,60 +1844,6 @@ describe('paginate', () => {
})
}
it('should return result based on virtualcolumn filter', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
filterableColumns: {
'home.countCat': [FilterOperator.GT],
},
relations: ['home'],
}
const query: PaginateQuery = {
path: '',
filter: {
'home.countCat': '$gt:0',
},
sortBy: [['id', 'ASC']],
}
const result = await paginate<CatEntity>(query, catRepo, config)
const expectedResult = [0, 1].map((i) => {
const ret = Object.assign(clone(cats[i]), { home: Object.assign(clone(catHomes[i])) })
delete ret.home.cat
return ret
})
expect(result.data).toStrictEqual(expectedResult)
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.home.countCat=$gt:0')
})
it('should return result sorted by a virtualcolumn', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['home.countCat'],
relations: ['home'],
}
const query: PaginateQuery = {
path: '',
sortBy: [['home.countCat', 'ASC']],
}
const result = await paginate<CatEntity>(query, catRepo, config)
const expectedResult = [2, 3, 4, 0, 1].map((i) => {
const ret = clone(cats[i])
if (i == 0 || i == 1) {
ret.home = clone(catHomes[i])
ret.home.countCat = cats.filter((cat) => cat.id === ret.home.cat.id).length
delete ret.home.cat
} else {
ret.home = null
}
return ret
})
expect(result.data).toStrictEqual(expectedResult)
expect(result.links.current).toBe('?page=1&limit=20&sortBy=home.countCat:ASC')
})
it('should return result based on or between range filter', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
@ -1922,104 +1884,6 @@ describe('paginate', () => {
)
})
it('should return result sorted and filter by a virtualcolumn in main entity', async () => {
const config: PaginateConfig<CatHomeEntity> = {
sortableColumns: ['countCat'],
relations: ['cat'],
filterableColumns: {
countCat: [FilterOperator.GT],
},
}
const query: PaginateQuery = {
path: '',
filter: {
countCat: '$gt:0',
},
sortBy: [['countCat', 'ASC']],
}
const result = await paginate<CatHomeEntity>(query, catHomeRepo, config)
expect(result.data).toStrictEqual([catHomes[0], catHomes[1]])
expect(result.links.current).toBe('?page=1&limit=20&sortBy=countCat:ASC&filter.countCat=$gt:0')
})
it('should return result based on virtualcolumn filter', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
filterableColumns: {
'home.countCat': [FilterOperator.GT],
},
relations: ['home'],
}
const query: PaginateQuery = {
path: '',
filter: {
'home.countCat': '$gt:0',
},
sortBy: [['id', 'ASC']],
}
const result = await paginate<CatEntity>(query, catRepo, config)
const expectedResult = [0, 1].map((i) => {
const ret = Object.assign(clone(cats[i]), { home: Object.assign(clone(catHomes[i])) })
delete ret.home.cat
return ret
})
expect(result.data).toStrictEqual(expectedResult)
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.home.countCat=$gt:0')
})
it('should return result sorted by a virtualcolumn', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['home.countCat'],
relations: ['home'],
}
const query: PaginateQuery = {
path: '',
sortBy: [['home.countCat', 'ASC']],
}
const result = await paginate<CatEntity>(query, catRepo, config)
const expectedResult = [2, 3, 4, 0, 1].map((i) => {
const ret = clone(cats[i])
if (i == 0 || i == 1) {
ret.home = clone(catHomes[i])
ret.home.countCat = cats.filter((cat) => cat.id === ret.home.cat.id).length
delete ret.home.cat
} else {
ret.home = null
}
return ret
})
expect(result.data).toStrictEqual(expectedResult)
expect(result.links.current).toBe('?page=1&limit=20&sortBy=home.countCat:ASC')
})
it('should return result sorted and filter by a virtualcolumn in main entity', async () => {
const config: PaginateConfig<CatHomeEntity> = {
sortableColumns: ['countCat'],
relations: ['cat'],
filterableColumns: {
countCat: [FilterOperator.GT],
},
}
const query: PaginateQuery = {
path: '',
filter: {
countCat: '$gt:0',
},
sortBy: [['countCat', 'ASC']],
}
const result = await paginate<CatHomeEntity>(query, catHomeRepo, config)
expect(result.data).toStrictEqual([catHomes[0], catHomes[1]])
expect(result.links.current).toBe('?page=1&limit=20&sortBy=countCat:ASC&filter.countCat=$gt:0')
})
it("should return all columns if select doesn't contain all primary columns", async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id', 'name'],
@ -2572,4 +2436,160 @@ describe('paginate', () => {
}
})
})
if (process.env.DB !== 'postgres') {
describe('should return result based on virtual column', () => {
it('should return result sorted and filter by a virtual column in main entity', async () => {
const config: PaginateConfig<CatHomeEntity> = {
sortableColumns: ['countCat'],
relations: ['cat'],
filterableColumns: {
countCat: [FilterOperator.GT],
},
}
const query: PaginateQuery = {
path: '',
filter: {
countCat: '$gt:0',
},
sortBy: [['countCat', 'ASC']],
}
const result = await paginate<CatHomeEntity>(query, catHomeRepo, config)
expect(result.data).toStrictEqual([catHomes[0], catHomes[1]])
expect(result.links.current).toBe('?page=1&limit=20&sortBy=countCat:ASC&filter.countCat=$gt:0')
})
it('should return result based on virtual column filter', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
filterableColumns: {
'home.countCat': [FilterOperator.GT],
},
relations: ['home'],
}
const query: PaginateQuery = {
path: '',
filter: {
'home.countCat': '$gt:0',
},
sortBy: [['id', 'ASC']],
}
const result = await paginate<CatEntity>(query, catRepo, config)
const expectedResult = [0, 1].map((i) => {
const ret = Object.assign(clone(cats[i]), { home: Object.assign(clone(catHomes[i])) })
delete ret.home.cat
return ret
})
expect(result.data).toStrictEqual(expectedResult)
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.home.countCat=$gt:0')
})
it('should return result sorted by a virtual column', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['home.countCat'],
relations: ['home'],
}
const query: PaginateQuery = {
path: '',
sortBy: [['home.countCat', 'ASC']],
}
const result = await paginate<CatEntity>(query, catRepo, config)
const expectedResult = [2, 3, 4, 0, 1].map((i) => {
const ret = clone(cats[i])
if (i == 0 || i == 1) {
ret.home = clone(catHomes[i])
ret.home.countCat = cats.filter((cat) => cat.id === ret.home.cat.id).length
delete ret.home.cat
} else {
ret.home = null
}
return ret
})
expect(result.data).toStrictEqual(expectedResult)
expect(result.links.current).toBe('?page=1&limit=20&sortBy=home.countCat:ASC')
})
it('should return result sorted and filter by a virtual column in main entity', async () => {
const config: PaginateConfig<CatHomeEntity> = {
sortableColumns: ['countCat'],
relations: ['cat'],
filterableColumns: {
countCat: [FilterOperator.GT],
},
}
const query: PaginateQuery = {
path: '',
filter: {
countCat: '$gt:0',
},
sortBy: [['countCat', 'ASC']],
}
const result = await paginate<CatHomeEntity>(query, catHomeRepo, config)
expect(result.data).toStrictEqual([catHomes[0], catHomes[1]])
expect(result.links.current).toBe('?page=1&limit=20&sortBy=countCat:ASC&filter.countCat=$gt:0')
})
it('should return result based on virtual column filter', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
filterableColumns: {
'home.countCat': [FilterOperator.GT],
},
relations: ['home'],
}
const query: PaginateQuery = {
path: '',
filter: {
'home.countCat': '$gt:0',
},
sortBy: [['id', 'ASC']],
}
const result = await paginate<CatEntity>(query, catRepo, config)
const expectedResult = [0, 1].map((i) => {
const ret = Object.assign(clone(cats[i]), { home: Object.assign(clone(catHomes[i])) })
delete ret.home.cat
return ret
})
expect(result.data).toStrictEqual(expectedResult)
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.home.countCat=$gt:0')
})
it('should return result sorted by a virtual column', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['home.countCat'],
relations: ['home'],
}
const query: PaginateQuery = {
path: '',
sortBy: [['home.countCat', 'ASC']],
}
const result = await paginate<CatEntity>(query, catRepo, config)
const expectedResult = [2, 3, 4, 0, 1].map((i) => {
const ret = clone(cats[i])
if (i == 0 || i == 1) {
ret.home = clone(catHomes[i])
ret.home.countCat = cats.filter((cat) => cat.id === ret.home.cat.id).length
delete ret.home.cat
} else {
ret.home = null
}
return ret
})
expect(result.data).toStrictEqual(expectedResult)
expect(result.links.current).toBe('?page=1&limit=20&sortBy=home.countCat:ASC')
})
})
}
})

View File

@ -114,11 +114,11 @@ export async function paginate<T extends ObjectLiteral>(
if (isPaginated) {
// Allow user to choose between limit/offset and take/skip.
// However, using take/skip can cause problems with sorting and selections.
if (config.paginationType === PaginationType.TAKE_AND_SKIP) {
queryBuilder.take(limit).skip((page - 1) * limit)
} else {
// However, using limit/offset can cause problems when joining one-to-many etc.
if (config.paginationType === PaginationType.LIMIT_AND_OFFSET) {
queryBuilder.limit(limit).offset((page - 1) * limit)
} else {
queryBuilder.take(limit).skip((page - 1) * limit)
}
}