feat: add searchBy functionality (#99)
This commit is contained in:
parent
e5eba96dfe
commit
7695096a60
9230
package-lock.json
generated
9230
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -43,6 +43,7 @@ describe('Decorator', () => {
|
||||
limit: undefined,
|
||||
sortBy: undefined,
|
||||
search: undefined,
|
||||
searchBy: undefined,
|
||||
filter: undefined,
|
||||
path: 'http://localhost/items',
|
||||
})
|
||||
@ -68,6 +69,7 @@ describe('Decorator', () => {
|
||||
['createdAt', 'DESC'],
|
||||
],
|
||||
search: 'white',
|
||||
searchBy: undefined,
|
||||
path: 'http://localhost/items',
|
||||
filter: {
|
||||
name: '$not:$eq:Kitty',
|
||||
|
@ -6,6 +6,7 @@ export interface PaginateQuery {
|
||||
page?: number
|
||||
limit?: number
|
||||
sortBy?: [string, string][]
|
||||
searchBy?: string[]
|
||||
search?: string
|
||||
filter?: { [column: string]: string | string[] }
|
||||
path: string
|
||||
@ -17,6 +18,8 @@ export const Paginate = createParamDecorator((_data: unknown, ctx: ExecutionCont
|
||||
const path = request.protocol + '://' + request.get('host') + request.baseUrl + request.path
|
||||
|
||||
const sortBy: [string, string][] = []
|
||||
const searchBy: string[] = []
|
||||
|
||||
if (query.sortBy) {
|
||||
const params = !Array.isArray(query.sortBy) ? [query.sortBy] : query.sortBy
|
||||
for (const param of params) {
|
||||
@ -29,6 +32,15 @@ export const Paginate = createParamDecorator((_data: unknown, ctx: ExecutionCont
|
||||
}
|
||||
}
|
||||
|
||||
if (query.searchBy) {
|
||||
const params = !Array.isArray(query.searchBy) ? [query.searchBy] : query.searchBy
|
||||
for (const param of params) {
|
||||
if (isString(param)) {
|
||||
searchBy.push(param)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const filter = mapKeys(
|
||||
pickBy(
|
||||
query,
|
||||
@ -44,6 +56,7 @@ export const Paginate = createParamDecorator((_data: unknown, ctx: ExecutionCont
|
||||
limit: query.limit ? parseInt(query.limit.toString(), 10) : undefined,
|
||||
sortBy: sortBy.length ? sortBy : undefined,
|
||||
search: query.search ? query.search.toString() : undefined,
|
||||
searchBy: searchBy.length ? searchBy : undefined,
|
||||
filter: Object.keys(filter).length ? filter : undefined,
|
||||
path,
|
||||
}
|
||||
|
@ -165,6 +165,29 @@ describe('paginate', () => {
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&search=i')
|
||||
})
|
||||
|
||||
it('should return result based on search term and searchBy columns', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id', 'name', 'color'],
|
||||
searchableColumns: ['name', 'color'],
|
||||
}
|
||||
|
||||
const searchTerm = 'white'
|
||||
const expectedResultData = cats.filter((cat: CatEntity) => cat.color === searchTerm)
|
||||
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
search: searchTerm,
|
||||
searchBy: ['color'],
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, repo, config)
|
||||
|
||||
expect(result.meta.search).toStrictEqual(searchTerm)
|
||||
expect(result.meta.searchBy).toStrictEqual(['color'])
|
||||
expect(result.data).toStrictEqual(expectedResultData)
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&search=white&searchBy=color')
|
||||
})
|
||||
|
||||
it('should return result based on where config and filter', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id'],
|
||||
|
@ -32,6 +32,7 @@ export class Paginated<T> {
|
||||
currentPage: number
|
||||
totalPages: number
|
||||
sortBy: SortBy<T>
|
||||
searchBy: Column<T>[]
|
||||
search: string
|
||||
filter?: { [column: string]: string | string[] }
|
||||
}
|
||||
@ -73,13 +74,14 @@ export async function paginate<T>(
|
||||
let page = query.page || 1
|
||||
const limit = Math.min(query.limit || config.defaultLimit || 20, config.maxLimit || 100)
|
||||
const sortBy = [] as SortBy<T>
|
||||
const searchBy: Column<T>[] = []
|
||||
const path = query.path
|
||||
|
||||
function isEntityKey(sortableColumns: Column<T>[], column: string): column is Column<T> {
|
||||
return !!sortableColumns.find((c) => c === column)
|
||||
function isEntityKey(entityColumns: Column<T>[], column: string): column is Column<T> {
|
||||
return !!entityColumns.find((c) => c === column)
|
||||
}
|
||||
|
||||
const { sortableColumns } = config
|
||||
const { sortableColumns, searchableColumns } = config
|
||||
if (config.sortableColumns.length < 1) throw new ServiceUnavailableException()
|
||||
|
||||
if (query.sortBy) {
|
||||
@ -89,10 +91,21 @@ export async function paginate<T>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sortBy.length) {
|
||||
sortBy.push(...(config.defaultSortBy || [[sortableColumns[0], 'ASC']]))
|
||||
}
|
||||
|
||||
if (query.searchBy && searchableColumns) {
|
||||
for (const column of query.searchBy) {
|
||||
if (isEntityKey(searchableColumns, column)) {
|
||||
searchBy.push(column as Column<T>)
|
||||
}
|
||||
}
|
||||
} else if (!query.searchBy && searchableColumns) {
|
||||
searchBy.push(...searchableColumns)
|
||||
}
|
||||
|
||||
if (page < 1) page = 1
|
||||
|
||||
let [items, totalItems]: [T[], number] = [[], 0]
|
||||
@ -120,9 +133,9 @@ export async function paginate<T>(
|
||||
queryBuilder = queryBuilder.andWhere(new Brackets((queryBuilder) => queryBuilder.andWhere(config.where))) // Postgres fix (https://github.com/ppetzold/nestjs-paginate/pull/97)
|
||||
}
|
||||
|
||||
if (query.search && config.searchableColumns) {
|
||||
if (query.search && searchBy.length) {
|
||||
const search: ObjectLiteral[] = []
|
||||
for (const column of config.searchableColumns) {
|
||||
for (const column of searchBy) {
|
||||
search.push({ [column]: ILike(`%${query.search}%`) })
|
||||
}
|
||||
queryBuilder = queryBuilder.andWhere(search)
|
||||
@ -203,6 +216,12 @@ export async function paginate<T>(
|
||||
|
||||
const sortByQuery = sortBy.map((order) => `&sortBy=${order.join(':')}`).join('')
|
||||
const searchQuery = query.search ? `&search=${query.search}` : ''
|
||||
|
||||
const searchByQuery =
|
||||
query.search && query.searchBy && searchBy.length
|
||||
? searchBy.map((column) => `&searchBy=${column}`).join('')
|
||||
: ''
|
||||
|
||||
const filterQuery = query.filter
|
||||
? '&' +
|
||||
stringify(
|
||||
@ -213,7 +232,7 @@ export async function paginate<T>(
|
||||
)
|
||||
: ''
|
||||
|
||||
const options = `&limit=${limit}${sortByQuery}${searchQuery}${filterQuery}`
|
||||
const options = `&limit=${limit}${sortByQuery}${searchQuery}${filterQuery}${searchByQuery}`
|
||||
|
||||
const buildLink = (p: number): string => path + '?page=' + p + options
|
||||
|
||||
@ -226,6 +245,7 @@ export async function paginate<T>(
|
||||
totalPages: totalPages,
|
||||
sortBy,
|
||||
search: query.search,
|
||||
searchBy,
|
||||
filter: query.filter,
|
||||
},
|
||||
links: {
|
||||
|
Loading…
Reference in New Issue
Block a user