feat: support dot notation syntax for nested relations (#739)

This commit is contained in:
Robin De Schepper 2023-09-09 18:41:35 +02:00 committed by GitHub
parent 2e376ad656
commit 6f6b4da9a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 33 deletions

View File

@ -710,7 +710,7 @@ describe('paginate', () => {
expect(result.links.current).toBe('?page=1&limit=20&sortBy=id:ASC&search=Garfield')
})
it('should load nested relations', async () => {
it('should load nested relations (object notation)', async () => {
const config: PaginateConfig<CatEntity> = {
relations: { home: { pillows: true } },
sortableColumns: ['id', 'name'],
@ -743,6 +743,39 @@ describe('paginate', () => {
expect(result.data[0].home.pillows).toStrictEqual(cat.home.pillows)
})
it('should load nested relations (array notation)', async () => {
const config: PaginateConfig<CatEntity> = {
relations: ['home.pillows'],
sortableColumns: ['id', 'name'],
searchableColumns: ['name'],
}
const query: PaginateQuery = {
path: '',
search: 'Garfield',
}
const result = await paginate<CatEntity>(query, catRepo, config)
const cat = clone(cats[1])
const catHomesClone = clone(catHomes[1])
const catHomePillowsClone3 = clone(catHomePillows[3])
delete catHomePillowsClone3.home
const catHomePillowsClone4 = clone(catHomePillows[4])
delete catHomePillowsClone4.home
const catHomePillowsClone5 = clone(catHomePillows[5])
delete catHomePillowsClone5.home
catHomesClone.countCat = cats.filter((cat) => cat.id === catHomesClone.cat.id).length
catHomesClone.pillows = [catHomePillowsClone3, catHomePillowsClone4, catHomePillowsClone5]
cat.home = catHomesClone
delete cat.home.cat
expect(result.meta.search).toStrictEqual('Garfield')
expect(result.data).toStrictEqual([cat])
expect(result.data[0].home).toBeDefined()
expect(result.data[0].home.pillows).toStrictEqual(cat.home.pillows)
})
it('should throw an error when nonexistent relation loaded', async () => {
const config: PaginateConfig<CatEntity> = {
relations: <any>['homee'],

View File

@ -6,6 +6,7 @@ import {
FindOptionsRelations,
ObjectLiteral,
FindOptionsUtils,
FindOptionsRelationByString,
} from 'typeorm'
import { PaginateQuery } from './decorator'
import { ServiceUnavailableException, Logger } from '@nestjs/common'
@ -28,6 +29,7 @@ import {
getQueryUrlComponents,
} from './helper'
import { addFilter, FilterOperator, FilterSuffix } from './filter'
import { OrmUtils } from 'typeorm/util/OrmUtils'
const logger: Logger = new Logger('nestjs-paginate')
@ -61,7 +63,7 @@ export enum PaginationType {
}
export interface PaginateConfig<T> {
relations?: FindOptionsRelations<T> | RelationColumn<T>[]
relations?: FindOptionsRelations<T> | RelationColumn<T>[] | FindOptionsRelationByString
sortableColumns: Column<T>[]
nullSort?: 'first' | 'last'
searchableColumns?: Column<T>[]
@ -123,41 +125,29 @@ export async function paginate<T extends ObjectLiteral>(
}
if (config.relations) {
// relations: ["relation"]
if (Array.isArray(config.relations)) {
config.relations.forEach((relation) => {
const relations = Array.isArray(config.relations)
? OrmUtils.propertyPathsToTruthyObject(config.relations)
: config.relations
const createQueryBuilderRelations = (
prefix: string,
relations: FindOptionsRelations<T> | RelationColumn<T>[],
alias?: string
) => {
Object.keys(relations).forEach((relationName) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const relationSchema = relations![relationName]!
queryBuilder.leftJoinAndSelect(
`${queryBuilder.alias}.${relation}`,
`${queryBuilder.alias}_${relation}_rel`
`${alias ?? prefix}.${relationName}`,
`${alias ?? prefix}_${relationName}_rel`
)
if (typeof relationSchema === 'object') {
createQueryBuilderRelations(relationName, relationSchema, `${alias ?? prefix}_${relationName}_rel`)
}
})
} else {
// relations: {relation:true}
const createQueryBuilderRelations = (
prefix: string,
relations: FindOptionsRelations<T> | RelationColumn<T>[],
alias?: string
) => {
Object.keys(relations).forEach((relationName) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const relationSchema = relations![relationName]!
queryBuilder.leftJoinAndSelect(
`${alias ?? prefix}.${relationName}`,
`${alias ?? prefix}_${relationName}_rel`
)
if (typeof relationSchema === 'object') {
createQueryBuilderRelations(
relationName,
relationSchema,
`${alias ?? prefix}_${relationName}_rel`
)
}
})
}
createQueryBuilderRelations(queryBuilder.alias, config.relations)
}
createQueryBuilderRelations(queryBuilder.alias, relations)
}
let nullSort: 'NULLS LAST' | 'NULLS FIRST' | undefined = undefined