feat: introduce starts with () operator (#466)

This commit is contained in:
Jacques Germishuys 2023-01-31 16:54:14 +00:00 committed by GitHub
parent 0e868adb30
commit 33df5f294f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 34 additions and 4 deletions

View File

@ -13,7 +13,7 @@ Pagination and filtering helper method for TypeORM repositories or query builder
- Pagination conforms to [JSON:API](https://jsonapi.org/) - Pagination conforms to [JSON:API](https://jsonapi.org/)
- Sort by multiple columns - Sort by multiple columns
- Search across columns - Search across columns
- Filter using operators (`$eq`, `$not`, `$null`, `$in`, `$gt`, `$gte`, `$lt`, `$lte`, `$btw`, `$ilike`) - Filter using operators (`$eq`, `$not`, `$null`, `$in`, `$gt`, `$gte`, `$lt`, `$lte`, `$btw`, `$ilike`, `$sw`)
- Include relations - Include relations
- Virtual column support - Virtual column support
@ -315,6 +315,8 @@ Filter operators must be whitelisted per column in `PaginateConfig`.
`?filter.summary=$not:$ilike:term` where column `summary` does **not** contain `term` `?filter.summary=$not:$ilike:term` where column `summary` does **not** contain `term`
`?filter.summary=$sw:term` where column `summary` starts with `term`
`?filter.seenAt=$null` where column `seenAt` is `NULL` `?filter.seenAt=$null` where column `seenAt` is `NULL`
`?filter.seenAt=$not:$null` where column `seenAt` is **not** `NULL` `?filter.seenAt=$not:$null` where column `seenAt` is **not** `NULL`

View File

@ -1281,17 +1281,40 @@ describe('paginate', () => {
const query: PaginateQuery = { const query: PaginateQuery = {
path: '', path: '',
filter: { filter: {
name: '$ilike:Garf', name: '$ilike:arf',
}, },
} }
const result = await paginate<CatEntity>(query, catRepo, config) const result = await paginate<CatEntity>(query, catRepo, config)
expect(result.meta.filter).toStrictEqual({ expect(result.meta.filter).toStrictEqual({
name: '$ilike:Garf', name: '$ilike:arf',
}) })
expect(result.data).toStrictEqual([cats[1]]) expect(result.data).toStrictEqual([cats[1]])
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.name=$ilike:Garf') expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.name=$ilike:arf')
})
it('should return result based on $sw filter', async () => {
const config: PaginateConfig<CatEntity> = {
sortableColumns: ['id'],
filterableColumns: {
name: [FilterOperator.SW],
},
}
const query: PaginateQuery = {
path: '',
filter: {
name: '$sw:Ga',
},
}
const result = await paginate<CatEntity>(query, catRepo, config)
expect(result.meta.filter).toStrictEqual({
name: '$sw:Ga',
})
expect(result.data).toStrictEqual([cats[1]])
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.name=$sw:Ga')
}) })
it('should return result based on filter and search term', async () => { it('should return result based on filter and search term', async () => {

View File

@ -84,6 +84,7 @@ export enum FilterOperator {
BTW = '$btw', BTW = '$btw',
NOT = '$not', NOT = '$not',
ILIKE = '$ilike', ILIKE = '$ilike',
SW = '$sw',
} }
export function isOperator(value: unknown): value is FilterOperator { export function isOperator(value: unknown): value is FilterOperator {
@ -101,6 +102,7 @@ export const OperatorSymbolToFunction = new Map<FilterOperator, (...args: any[])
[FilterOperator.BTW, Between], [FilterOperator.BTW, Between],
[FilterOperator.NOT, Not], [FilterOperator.NOT, Not],
[FilterOperator.ILIKE, ILike], [FilterOperator.ILIKE, ILike],
[FilterOperator.SW, ILike],
]) ])
export function getFilterTokens(raw: string): string[] { export function getFilterTokens(raw: string): string[] {
@ -169,6 +171,9 @@ function parseFilter<T>(query: PaginateQuery, config: PaginateConfig<T>): Filter
case FilterOperator.ILIKE: case FilterOperator.ILIKE:
filter[column] = OperatorSymbolToFunction.get(op1)(`%${value}%`) filter[column] = OperatorSymbolToFunction.get(op1)(`%${value}%`)
break break
case FilterOperator.SW:
filter[column] = OperatorSymbolToFunction.get(op1)(`${value}%`)
break
default: default:
filter[column] = OperatorSymbolToFunction.get(op1)(value) filter[column] = OperatorSymbolToFunction.get(op1)(value)
break break