feat: add searchBy functionality (#99)

This commit is contained in:
Istiyak Tailor 2021-10-12 16:31:53 +05:30 committed by GitHub
parent e5eba96dfe
commit 7695096a60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 9272 additions and 28 deletions

9230
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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',

View File

@ -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,
}

View File

@ -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'],

View File

@ -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: {