feat: nested search and filter (#509)
BREAKING CHANGE: Embedded fields have to wrapped in brackets e.g. `toys.(size.height)`.
This commit is contained in:
parent
5a72a53b2f
commit
339249ce24
27
README.md
27
README.md
@ -15,7 +15,7 @@ Pagination and filtering helper method for TypeORM repositories or query builder
|
||||
- Search across columns
|
||||
- Select columns
|
||||
- Filter using operators (`$eq`, `$not`, `$null`, `$in`, `$gt`, `$gte`, `$lt`, `$lte`, `$btw`, `$ilike`, `$sw`)
|
||||
- Include relations
|
||||
- Include relations and nested relations
|
||||
- Virtual column support
|
||||
|
||||
## Installation
|
||||
@ -351,6 +351,31 @@ const config: PaginateConfig<CatEntity> = {
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
```
|
||||
|
||||
## Usage with nested Relations
|
||||
|
||||
Similar as with relations, you can specify nested relations for sorting, filtering, search and relations:
|
||||
|
||||
### Example
|
||||
|
||||
#### Endpoint
|
||||
|
||||
```url
|
||||
http://localhost:3000/cats?filter.home.pillows.color=$eq:ping,String
|
||||
```
|
||||
|
||||
#### Code
|
||||
|
||||
```typescript
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
relations: { home: { pillows: true } },
|
||||
sortableColumns: ['id', 'name', 'home.pillows.color'],
|
||||
searchableColumns: ['name', 'home.pillows.color'],
|
||||
filterableColumns: { 'home.pillows.color': [FilterOperator.EQ] },
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
```
|
||||
|
||||
## Usage of pagination type
|
||||
|
||||
You can use either `limit`/`offset` or `take`/`skip` to return paginated results.
|
||||
|
@ -104,8 +104,14 @@ export function addWhereCondition<T>(qb: SelectQueryBuilder<T>, column: string,
|
||||
const isEmbedded = checkIsEmbedded(qb, columnProperties.propertyPath)
|
||||
const alias = fixColumnAlias(columnProperties, qb.alias, isRelation, isVirtualProperty, isEmbedded, virtualQuery)
|
||||
filter[column].forEach((columnFilter: Filter, index: number) => {
|
||||
const columnNamePerIteration = `${column}${index}`
|
||||
const condition = generatePredicateCondition(qb, column, columnFilter, alias, isVirtualProperty)
|
||||
const columnNamePerIteration = `${columnProperties.column}${index}`
|
||||
const condition = generatePredicateCondition(
|
||||
qb,
|
||||
columnProperties.column,
|
||||
columnFilter,
|
||||
alias,
|
||||
isVirtualProperty
|
||||
)
|
||||
const parameters = fixQueryParam(alias, columnNamePerIteration, columnFilter, condition, {
|
||||
[columnNamePerIteration]: columnFilter.findOperator.value,
|
||||
})
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { SelectQueryBuilder } from 'typeorm'
|
||||
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata'
|
||||
|
||||
type Join<K, P> = K extends string ? (P extends string ? `${K}${'' extends P ? '' : '.'}${P}` : never) : never
|
||||
type Join<K, P> = K extends string
|
||||
? P extends string
|
||||
? `${K}${'' extends P ? '' : '.'}${P | `(${P}` | `${P})`}`
|
||||
: never
|
||||
: never
|
||||
|
||||
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...0[]]
|
||||
|
||||
@ -33,16 +37,30 @@ export type SortBy<T> = Order<T>[]
|
||||
export const positiveNumberOrDefault = (value: number | undefined, defaultValue: number, minValue: 0 | 1 = 0) =>
|
||||
value === undefined || value < minValue ? defaultValue : value
|
||||
|
||||
export type ColumnProperties = { propertyPath?: string; propertyName: string }
|
||||
export type ColumnProperties = { propertyPath?: string; propertyName: string; isEmbedded: boolean; column: string }
|
||||
|
||||
export function getPropertiesByColumnName(column: string): ColumnProperties {
|
||||
const propertyPath = column.split('.')
|
||||
return propertyPath.length > 1
|
||||
? {
|
||||
propertyPath: propertyPath[0],
|
||||
propertyName: propertyPath.slice(1).join('.'), // the join is in case of an embedded entity
|
||||
}
|
||||
: { propertyName: propertyPath[0] }
|
||||
if (propertyPath.length > 1) {
|
||||
const propertyNamePath = propertyPath.slice(1)
|
||||
let isEmbedded = false,
|
||||
propertyName = propertyNamePath.join('.')
|
||||
|
||||
if (!propertyName.startsWith('(') && propertyNamePath.length > 1) {
|
||||
isEmbedded = true
|
||||
}
|
||||
|
||||
propertyName = propertyName.replace('(', '').replace(')', '')
|
||||
|
||||
return {
|
||||
propertyPath: propertyPath[0],
|
||||
propertyName, // the join is in case of an embedded entity
|
||||
isEmbedded,
|
||||
column: `${propertyPath[0]}.${propertyName}`,
|
||||
}
|
||||
} else {
|
||||
return { propertyName: propertyPath[0], isEmbedded: false, column: propertyPath[0] }
|
||||
}
|
||||
}
|
||||
|
||||
export function extractVirtualProperty(
|
||||
@ -106,7 +124,7 @@ export function fixColumnAlias(
|
||||
if (isRelation) {
|
||||
if (isVirtualProperty && query) {
|
||||
return `(${query(`${alias}_${properties.propertyPath}`)})` // () is needed to avoid parameter conflict
|
||||
} else if (isVirtualProperty && !query) {
|
||||
} else if ((isVirtualProperty && !query) || properties.isEmbedded) {
|
||||
return `${alias}_${properties.propertyPath}_${properties.propertyName}`
|
||||
} else {
|
||||
return `${alias}_${properties.propertyPath}.${properties.propertyName}`
|
||||
|
@ -882,7 +882,7 @@ describe('paginate', () => {
|
||||
|
||||
it('should return result based on sort on embedded entity on one-to-many relation', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id', 'name', 'toys.size.height', 'toys.size.length', 'toys.size.width'],
|
||||
sortableColumns: ['id', 'name', 'toys.(size.height)', 'toys.(size.length)', 'toys.(size.width)'],
|
||||
searchableColumns: ['name'],
|
||||
relations: ['toys'],
|
||||
}
|
||||
@ -890,8 +890,8 @@ describe('paginate', () => {
|
||||
path: '',
|
||||
sortBy: [
|
||||
['id', 'DESC'],
|
||||
['toys.size.height', 'ASC'],
|
||||
['toys.size.length', 'ASC'],
|
||||
['toys.(size.height)', 'ASC'],
|
||||
['toys.(size.length)', 'ASC'],
|
||||
],
|
||||
}
|
||||
|
||||
@ -918,21 +918,21 @@ describe('paginate', () => {
|
||||
]
|
||||
expect(result.data).toStrictEqual(orderedCats)
|
||||
expect(result.links.current).toBe(
|
||||
'?page=1&limit=20&sortBy=id:DESC&sortBy=toys.size.height:ASC&sortBy=toys.size.length:ASC'
|
||||
'?page=1&limit=20&sortBy=id:DESC&sortBy=toys.(size.height):ASC&sortBy=toys.(size.length):ASC'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return result based on sort on embedded entity on many-to-one relation', async () => {
|
||||
const config: PaginateConfig<CatToyEntity> = {
|
||||
sortableColumns: ['id', 'name', 'cat.size.height', 'cat.size.length', 'cat.size.width'],
|
||||
sortableColumns: ['id', 'name', 'cat.(size.height)', 'cat.(size.length)', 'cat.(size.width)'],
|
||||
searchableColumns: ['name'],
|
||||
relations: ['cat'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
sortBy: [
|
||||
['cat.size.height', 'DESC'],
|
||||
['cat.size.length', 'DESC'],
|
||||
['cat.(size.height)', 'DESC'],
|
||||
['cat.(size.length)', 'DESC'],
|
||||
['name', 'ASC'],
|
||||
],
|
||||
}
|
||||
@ -942,19 +942,19 @@ describe('paginate', () => {
|
||||
|
||||
expect(result.data).toStrictEqual(orderedToys)
|
||||
expect(result.links.current).toBe(
|
||||
'?page=1&limit=20&sortBy=cat.size.height:DESC&sortBy=cat.size.length:DESC&sortBy=name:ASC'
|
||||
'?page=1&limit=20&sortBy=cat.(size.height):DESC&sortBy=cat.(size.length):DESC&sortBy=name:ASC'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return result based on sort on embedded entity on one-to-one relation', async () => {
|
||||
const config: PaginateConfig<CatHomeEntity> = {
|
||||
sortableColumns: ['id', 'name', 'cat.size.height', 'cat.size.length', 'cat.size.width'],
|
||||
sortableColumns: ['id', 'name', 'cat.(size.height)', 'cat.(size.length)', 'cat.(size.width)'],
|
||||
searchableColumns: ['name'],
|
||||
relations: ['cat'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
sortBy: [['cat.size.height', 'DESC']],
|
||||
sortBy: [['cat.(size.height)', 'DESC']],
|
||||
}
|
||||
|
||||
const result = await paginate<CatHomeEntity>(query, catHomeRepo, config)
|
||||
@ -964,7 +964,7 @@ describe('paginate', () => {
|
||||
orderedHomes[1].countCat = cats.filter((cat) => cat.id === orderedHomes[1].cat.id).length
|
||||
|
||||
expect(result.data).toStrictEqual(orderedHomes)
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=cat.size.height:DESC')
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=cat.(size.height):DESC')
|
||||
})
|
||||
|
||||
it('should return result based on search on embedded entity', async () => {
|
||||
@ -1008,8 +1008,8 @@ describe('paginate', () => {
|
||||
|
||||
it('should return result based on search term on embedded entity on many-to-one relation', async () => {
|
||||
const config: PaginateConfig<CatToyEntity> = {
|
||||
sortableColumns: ['id', 'name', 'cat.size.height', 'cat.size.length', 'cat.size.width'],
|
||||
searchableColumns: ['cat.size.height'],
|
||||
sortableColumns: ['id', 'name', 'cat.(size.height)', 'cat.(size.length)', 'cat.(size.width)'],
|
||||
searchableColumns: ['cat.(size.height)'],
|
||||
relations: ['cat'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
@ -1025,8 +1025,8 @@ describe('paginate', () => {
|
||||
|
||||
it('should return result based on search term on embedded entity on one-to-many relation', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id', 'name', 'toys.size.height', 'toys.size.length', 'toys.size.width'],
|
||||
searchableColumns: ['toys.size.height'],
|
||||
sortableColumns: ['id', 'name', 'toys.(size.height)', 'toys.(size.length)', 'toys.(size.width)'],
|
||||
searchableColumns: ['toys.(size.height)'],
|
||||
relations: ['toys'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
@ -1051,8 +1051,8 @@ describe('paginate', () => {
|
||||
|
||||
it('should return result based on search term on embedded entity on one-to-one relation', async () => {
|
||||
const config: PaginateConfig<CatHomeEntity> = {
|
||||
sortableColumns: ['id', 'name', 'cat.size.height', 'cat.size.length', 'cat.size.width'],
|
||||
searchableColumns: ['cat.size.height'],
|
||||
sortableColumns: ['id', 'name', 'cat.(size.height)', 'cat.(size.length)', 'cat.(size.width)'],
|
||||
searchableColumns: ['cat.(size.height)'],
|
||||
relations: ['cat'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
@ -1069,20 +1069,20 @@ describe('paginate', () => {
|
||||
|
||||
it('should return result based on sort and search on embedded many-to-one relation', async () => {
|
||||
const config: PaginateConfig<CatToyEntity> = {
|
||||
sortableColumns: ['id', 'name', 'cat.size.height', 'cat.size.length', 'cat.size.width'],
|
||||
searchableColumns: ['cat.size.width'],
|
||||
sortableColumns: ['id', 'name', 'cat.(size.height)', 'cat.(size.length)', 'cat.(size.width)'],
|
||||
searchableColumns: ['cat.(size.width)'],
|
||||
relations: ['cat'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
search: '1',
|
||||
sortBy: [['cat.size.height', 'DESC']],
|
||||
sortBy: [['cat.(size.height)', 'DESC']],
|
||||
}
|
||||
|
||||
const result = await paginate<CatToyEntity>(query, catToyRepo, config)
|
||||
expect(result.meta.search).toStrictEqual('1')
|
||||
expect(result.data).toStrictEqual([catToys[3], catToys[0], catToys[1], catToys[2]])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=cat.size.height:DESC&search=1')
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=cat.(size.height):DESC&search=1')
|
||||
})
|
||||
|
||||
it('should return result based on filter on embedded entity', async () => {
|
||||
@ -1143,23 +1143,23 @@ describe('paginate', () => {
|
||||
relations: ['cat'],
|
||||
sortableColumns: ['id', 'name'],
|
||||
filterableColumns: {
|
||||
'cat.size.height': [FilterSuffix.NOT],
|
||||
'cat.(size.height)': [FilterSuffix.NOT],
|
||||
},
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
filter: {
|
||||
'cat.size.height': '$not:25',
|
||||
'cat.(size.height)': '$not:25',
|
||||
},
|
||||
}
|
||||
|
||||
const result = await paginate<CatToyEntity>(query, catToyRepo, config)
|
||||
|
||||
expect(result.meta.filter).toStrictEqual({
|
||||
'cat.size.height': '$not:25',
|
||||
'cat.(size.height)': '$not:25',
|
||||
})
|
||||
expect(result.data).toStrictEqual([catToys[3]])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.cat.size.height=$not:25')
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.cat.(size.height)=$not:25')
|
||||
})
|
||||
|
||||
it('should return result based on filter on embedded on one-to-many relation', async () => {
|
||||
@ -1167,13 +1167,13 @@ describe('paginate', () => {
|
||||
relations: ['toys'],
|
||||
sortableColumns: ['id', 'name'],
|
||||
filterableColumns: {
|
||||
'toys.size.height': [FilterOperator.EQ],
|
||||
'toys.(size.height)': [FilterOperator.EQ],
|
||||
},
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
filter: {
|
||||
'toys.size.height': '1',
|
||||
'toys.(size.height)': '1',
|
||||
},
|
||||
}
|
||||
|
||||
@ -1185,10 +1185,10 @@ describe('paginate', () => {
|
||||
cat2.toys = [catToys3]
|
||||
|
||||
expect(result.meta.filter).toStrictEqual({
|
||||
'toys.size.height': '1',
|
||||
'toys.(size.height)': '1',
|
||||
})
|
||||
expect(result.data).toStrictEqual([cat2])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.toys.size.height=1')
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.toys.(size.height)=1')
|
||||
})
|
||||
|
||||
it('should return result based on filter on embedded on one-to-one relation', async () => {
|
||||
@ -1196,25 +1196,25 @@ describe('paginate', () => {
|
||||
relations: ['cat'],
|
||||
sortableColumns: ['id', 'name'],
|
||||
filterableColumns: {
|
||||
'cat.size.height': [FilterOperator.EQ],
|
||||
'cat.(size.height)': [FilterOperator.EQ],
|
||||
},
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
filter: {
|
||||
'cat.size.height': '$eq:30',
|
||||
'cat.(size.height)': '$eq:30',
|
||||
},
|
||||
}
|
||||
|
||||
const result = await paginate<CatHomeEntity>(query, catHomeRepo, config)
|
||||
|
||||
expect(result.meta.filter).toStrictEqual({
|
||||
'cat.size.height': '$eq:30',
|
||||
'cat.(size.height)': '$eq:30',
|
||||
})
|
||||
const catClone = clone(catHomes[1])
|
||||
catClone.countCat = cats.filter((cat) => cat.size.height === 30 && cat.id == catClone.cat.id).length
|
||||
expect(result.data).toStrictEqual([catClone])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.cat.size.height=$eq:30')
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.cat.(size.height)=$eq:30')
|
||||
})
|
||||
|
||||
it('should return result based on $in filter on embedded on one-to-one relation', async () => {
|
||||
@ -1222,20 +1222,20 @@ describe('paginate', () => {
|
||||
relations: ['cat'],
|
||||
sortableColumns: ['id', 'name'],
|
||||
filterableColumns: {
|
||||
'cat.size.height': [FilterOperator.IN],
|
||||
'cat.(size.height)': [FilterOperator.IN],
|
||||
},
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
filter: {
|
||||
'cat.size.height': '$in:10,30,35',
|
||||
'cat.(size.height)': '$in:10,30,35',
|
||||
},
|
||||
}
|
||||
|
||||
const result = await paginate<CatHomeEntity>(query, catHomeRepo, config)
|
||||
|
||||
expect(result.meta.filter).toStrictEqual({
|
||||
'cat.size.height': '$in:10,30,35',
|
||||
'cat.(size.height)': '$in:10,30,35',
|
||||
})
|
||||
const catClone = clone(catHomes[1])
|
||||
catClone.countCat = cats.filter(
|
||||
@ -1244,7 +1244,7 @@ describe('paginate', () => {
|
||||
cat.id == catClone.cat.id
|
||||
).length
|
||||
expect(result.data).toStrictEqual([catClone])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.cat.size.height=$in:10,30,35')
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.cat.(size.height)=$in:10,30,35')
|
||||
})
|
||||
|
||||
it('should return result based on $btw filter on embedded on one-to-one relation', async () => {
|
||||
@ -1252,20 +1252,20 @@ describe('paginate', () => {
|
||||
relations: ['cat'],
|
||||
sortableColumns: ['id', 'name'],
|
||||
filterableColumns: {
|
||||
'cat.size.height': [FilterOperator.BTW],
|
||||
'cat.(size.height)': [FilterOperator.BTW],
|
||||
},
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
filter: {
|
||||
'cat.size.height': '$btw:18,33',
|
||||
'cat.(size.height)': '$btw:18,33',
|
||||
},
|
||||
}
|
||||
|
||||
const result = await paginate<CatHomeEntity>(query, catHomeRepo, config)
|
||||
|
||||
expect(result.meta.filter).toStrictEqual({
|
||||
'cat.size.height': '$btw:18,33',
|
||||
'cat.(size.height)': '$btw:18,33',
|
||||
})
|
||||
|
||||
const catHomeClone = clone(catHomes)
|
||||
@ -1276,7 +1276,7 @@ describe('paginate', () => {
|
||||
(cat) => cat.size.height >= 18 && cat.size.height <= 33 && cat.id == catHomeClone[1].cat.id
|
||||
).length
|
||||
expect(result.data).toStrictEqual([catHomeClone[0], catHomeClone[1]])
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.cat.size.height=$btw:18,33')
|
||||
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&filter.cat.(size.height)=$btw:18,33')
|
||||
})
|
||||
|
||||
it('should return result based on where array and filter', async () => {
|
||||
@ -1954,12 +1954,12 @@ describe('paginate', () => {
|
||||
it('should return selected columns', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id', 'name'],
|
||||
select: ['id', 'name', 'toys.name', 'toys.size.height', 'toys.size.length'],
|
||||
select: ['id', 'name', 'toys.name', 'toys.(size.height)', 'toys.(size.length)'],
|
||||
relations: ['toys'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
select: ['id', 'toys.size.height'],
|
||||
select: ['id', 'toys.(size.height)'],
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
@ -2015,4 +2015,62 @@ describe('paginate', () => {
|
||||
|
||||
expect(result.data[0].toys).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should search nested relations', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
relations: { home: { pillows: true } },
|
||||
sortableColumns: ['id', 'name'],
|
||||
searchableColumns: ['name', 'home.pillows.color'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
search: 'pink',
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
|
||||
const cat = clone(cats[1])
|
||||
const catHomesClone = clone(catHomes[1])
|
||||
const catHomePillowsClone = clone(catHomePillows[3])
|
||||
delete catHomePillowsClone.home
|
||||
|
||||
catHomesClone.countCat = cats.filter((cat) => cat.id === catHomesClone.cat.id).length
|
||||
catHomesClone.pillows = [catHomePillowsClone]
|
||||
cat.home = catHomesClone
|
||||
delete cat.home.cat
|
||||
|
||||
expect(result.meta.search).toStrictEqual('pink')
|
||||
expect(result.data).toStrictEqual([cat])
|
||||
expect(result.data[0].home).toBeDefined()
|
||||
expect(result.data[0].home.pillows).toStrictEqual(cat.home.pillows)
|
||||
})
|
||||
|
||||
it('should filter nested relations', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
relations: { home: { pillows: true } },
|
||||
sortableColumns: ['id', 'name'],
|
||||
filterableColumns: { 'home.pillows.color': [FilterOperator.EQ] },
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
filter: { 'home.pillows.color': 'pink' },
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
|
||||
const cat = clone(cats[1])
|
||||
const catHomesClone = clone(catHomes[1])
|
||||
const catHomePillowsClone = clone(catHomePillows[3])
|
||||
delete catHomePillowsClone.home
|
||||
|
||||
catHomesClone.countCat = cats.filter((cat) => cat.id === catHomesClone.cat.id).length
|
||||
catHomesClone.pillows = [catHomePillowsClone]
|
||||
cat.home = catHomesClone
|
||||
delete cat.home.cat
|
||||
|
||||
expect(result.meta.filter['home.pillows.color']).toStrictEqual('pink')
|
||||
expect(result.data).toStrictEqual([cat])
|
||||
expect(result.data[0].home).toBeDefined()
|
||||
expect(result.data[0].home.pillows).toStrictEqual(cat.home.pillows)
|
||||
})
|
||||
})
|
||||
|
@ -62,13 +62,13 @@ export interface PaginateConfig<T> {
|
||||
sortableColumns: Column<T>[]
|
||||
nullSort?: 'first' | 'last'
|
||||
searchableColumns?: Column<T>[]
|
||||
select?: Column<T>[]
|
||||
select?: Column<T>[] | string[]
|
||||
maxLimit?: number
|
||||
defaultSortBy?: SortBy<T>
|
||||
defaultLimit?: number
|
||||
where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]
|
||||
filterableColumns?: {
|
||||
[key in Column<T>]?: (FilterOperator | FilterSuffix)[]
|
||||
[key in Column<T> | string]?: (FilterOperator | FilterSuffix)[]
|
||||
}
|
||||
loadEagerRelations?: boolean
|
||||
withDeleted?: boolean
|
||||
@ -264,9 +264,10 @@ export async function paginate<T extends ObjectLiteral>(
|
||||
isEmbeded,
|
||||
virtualQuery
|
||||
)
|
||||
|
||||
const condition: WherePredicateOperator = {
|
||||
operator: 'ilike',
|
||||
parameters: [alias, `:${column}`],
|
||||
parameters: [alias, `:${property.column}`],
|
||||
}
|
||||
|
||||
if (['postgres', 'cockroachdb'].includes(queryBuilder.connection.options.type)) {
|
||||
@ -274,7 +275,7 @@ export async function paginate<T extends ObjectLiteral>(
|
||||
}
|
||||
|
||||
qb.orWhere(qb['createWhereConditionExpression'](condition), {
|
||||
[column]: `%${query.search}%`,
|
||||
[property.column]: `%${query.search}%`,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user