fix: many-to-many relations (#492)
This commit is contained in:
parent
02afb0a0d4
commit
53f18bd547
@ -5,6 +5,8 @@ import {
|
||||
DeleteDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
ManyToMany,
|
||||
OneToMany,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
@ -43,6 +45,10 @@ export class CatEntity {
|
||||
@DeleteDateColumn({ nullable: true })
|
||||
deletedAt?: string
|
||||
|
||||
@ManyToMany(() => CatEntity)
|
||||
@JoinTable()
|
||||
friends: CatEntity[]
|
||||
|
||||
@AfterLoad()
|
||||
// Fix due to typeorm bug that doesn't set entity to null
|
||||
// when the reletated entity have only the virtual column property with a value different from null
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { Brackets, FindOperator, SelectQueryBuilder } from 'typeorm'
|
||||
import { WherePredicateOperator } from 'typeorm/query-builder/WhereClause'
|
||||
import { PaginateQuery } from './decorator'
|
||||
import { checkIsRelation, extractVirtualProperty, fixColumnAlias, getPropertiesByColumnName } from './helper'
|
||||
import {
|
||||
checkIsEmbedded,
|
||||
checkIsRelation,
|
||||
extractVirtualProperty,
|
||||
fixColumnAlias,
|
||||
getPropertiesByColumnName,
|
||||
} from './helper'
|
||||
import {
|
||||
FilterComparator,
|
||||
FilterOperator,
|
||||
@ -95,9 +101,17 @@ export function addWhereCondition<T>(qb: SelectQueryBuilder<T>, column: string,
|
||||
const columnProperties = getPropertiesByColumnName(column)
|
||||
const { isVirtualProperty, query: virtualQuery } = extractVirtualProperty(qb, columnProperties)
|
||||
const isRelation = checkIsRelation(qb, columnProperties.propertyPath)
|
||||
const isEmbedded = checkIsEmbedded(qb, columnProperties.propertyPath)
|
||||
filter[column].forEach((columnFilter: Filter, index: number) => {
|
||||
const columnNamePerIteration = `${column}${index}`
|
||||
const alias = fixColumnAlias(columnProperties, qb.alias, isRelation, isVirtualProperty, virtualQuery)
|
||||
const alias = fixColumnAlias(
|
||||
columnProperties,
|
||||
qb.alias,
|
||||
isRelation,
|
||||
isVirtualProperty,
|
||||
isEmbedded,
|
||||
virtualQuery
|
||||
)
|
||||
const condition = generatePredicateCondition(qb, column, columnFilter, alias, isVirtualProperty)
|
||||
const parameters = fixQueryParam(alias, columnNamePerIteration, columnFilter, condition, {
|
||||
[columnNamePerIteration]: columnFilter.findOperator.value,
|
||||
|
@ -68,12 +68,20 @@ export function checkIsRelation(qb: SelectQueryBuilder<unknown>, propertyPath: s
|
||||
return !!qb?.expressionMap?.mainAlias?.metadata?.hasRelationWithPropertyPath(propertyPath)
|
||||
}
|
||||
|
||||
export function checkIsEmbedded(qb: SelectQueryBuilder<unknown>, propertyPath: string): boolean {
|
||||
if (!qb || !propertyPath) {
|
||||
return false
|
||||
}
|
||||
return !!qb?.expressionMap?.mainAlias?.metadata?.hasEmbeddedWithPropertyPath(propertyPath)
|
||||
}
|
||||
|
||||
// This function is used to fix the column alias when using relation, embedded or virtual properties
|
||||
export function fixColumnAlias(
|
||||
properties: ColumnProperties,
|
||||
alias: string,
|
||||
isRelation = false,
|
||||
isVirtualProperty = false,
|
||||
isEmbedded = false,
|
||||
query?: ColumnMetadata['query']
|
||||
): string {
|
||||
if (isRelation) {
|
||||
@ -82,11 +90,13 @@ export function fixColumnAlias(
|
||||
} else if (isVirtualProperty && !query) {
|
||||
return `${alias}_${properties.propertyPath}_${properties.propertyName}`
|
||||
} else {
|
||||
return `${alias}_${properties.propertyPath}.${properties.propertyName}` // include embeded property and relation property
|
||||
return `${alias}_${properties.propertyPath}.${properties.propertyName}`
|
||||
}
|
||||
} else if (isVirtualProperty) {
|
||||
return query ? `(${query(`${alias}`)})` : `${alias}_${properties.propertyName}`
|
||||
} else if (isEmbedded) {
|
||||
return `${alias}.${properties.propertyPath}.${properties.propertyName}`
|
||||
} else {
|
||||
return `${alias}.${properties.propertyName}` //
|
||||
return `${alias}.${properties.propertyName}`
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ describe('paginate', () => {
|
||||
catHomeRepo.create({ name: 'Box', cat: cats[0] }),
|
||||
catHomeRepo.create({ name: 'House', cat: cats[1] }),
|
||||
])
|
||||
|
||||
// add friends to Milo
|
||||
catRepo.save({ ...cats[0], friends: cats.slice(1) })
|
||||
})
|
||||
|
||||
it('should return an instance of Paginated', async () => {
|
||||
@ -462,9 +465,9 @@ describe('paginate', () => {
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
|
||||
expect(result.meta.search).toStrictEqual('Mouse')
|
||||
const toy = clone(catToys[1])
|
||||
const toy = clone(catToys[2])
|
||||
delete toy.cat
|
||||
const toy2 = clone(catToys[2])
|
||||
const toy2 = clone(catToys[1])
|
||||
delete toy2.cat
|
||||
|
||||
expect(result.data).toStrictEqual([Object.assign(clone(cats[0]), { toys: [toy2, toy] })])
|
||||
@ -807,7 +810,7 @@ describe('paginate', () => {
|
||||
delete copy.cat
|
||||
return copy
|
||||
})
|
||||
copyCats[0].toys = [copyToys[0], copyToys[2], copyToys[1]]
|
||||
copyCats[0].toys = [copyToys[0], copyToys[1], copyToys[2]]
|
||||
copyCats[1].toys = [copyToys[3]]
|
||||
|
||||
const orderedCats = [copyCats[3], copyCats[1], copyCats[2], copyCats[0], copyCats[4]]
|
||||
@ -1872,4 +1875,19 @@ describe('paginate', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the right amount of results if a many to many relation is involved', async () => {
|
||||
const config: PaginateConfig<CatEntity> = {
|
||||
sortableColumns: ['id'],
|
||||
defaultSortBy: [['id', 'ASC']],
|
||||
relations: ['friends'],
|
||||
}
|
||||
const query: PaginateQuery = {
|
||||
path: '',
|
||||
}
|
||||
|
||||
const result = await paginate<CatEntity>(query, catRepo, config)
|
||||
|
||||
expect(result.data.length).toBe(4)
|
||||
})
|
||||
})
|
||||
|
@ -6,6 +6,7 @@ import { stringify } from 'querystring'
|
||||
import { WherePredicateOperator } from 'typeorm/query-builder/WhereClause'
|
||||
import {
|
||||
checkIsRelation,
|
||||
checkIsEmbedded,
|
||||
Column,
|
||||
extractVirtualProperty,
|
||||
fixColumnAlias,
|
||||
@ -142,8 +143,8 @@ export async function paginate<T extends ObjectLiteral>(
|
||||
// Switch from take and skip to limit and offset
|
||||
// due to this problem https://github.com/typeorm/typeorm/issues/5670
|
||||
// (anyway this creates more clean query without double dinstict)
|
||||
queryBuilder.limit(limit).offset((page - 1) * limit)
|
||||
// queryBuilder.take(limit).skip((page - 1) * limit)
|
||||
// queryBuilder.limit(limit).offset((page - 1) * limit)
|
||||
queryBuilder.take(limit).skip((page - 1) * limit)
|
||||
}
|
||||
|
||||
if (config.relations?.length) {
|
||||
@ -161,9 +162,8 @@ export async function paginate<T extends ObjectLiteral>(
|
||||
const columnProperties = getPropertiesByColumnName(order[0])
|
||||
const { isVirtualProperty } = extractVirtualProperty(queryBuilder, columnProperties)
|
||||
const isRelation = checkIsRelation(queryBuilder, columnProperties.propertyPath)
|
||||
|
||||
const alias = fixColumnAlias(columnProperties, queryBuilder.alias, isRelation, isVirtualProperty)
|
||||
|
||||
const isEmbeded = checkIsEmbedded(queryBuilder, columnProperties.propertyPath)
|
||||
const alias = fixColumnAlias(columnProperties, queryBuilder.alias, isRelation, isVirtualProperty, isEmbeded)
|
||||
queryBuilder.addOrderBy(alias, order[1], nullSort)
|
||||
}
|
||||
|
||||
@ -194,8 +194,15 @@ export async function paginate<T extends ObjectLiteral>(
|
||||
const property = getPropertiesByColumnName(column)
|
||||
const { isVirtualProperty, query: virtualQuery } = extractVirtualProperty(qb, property)
|
||||
const isRelation = checkIsRelation(qb, property.propertyPath)
|
||||
|
||||
const alias = fixColumnAlias(property, qb.alias, isRelation, isVirtualProperty, virtualQuery)
|
||||
const isEmbeded = checkIsEmbedded(qb, property.propertyPath)
|
||||
const alias = fixColumnAlias(
|
||||
property,
|
||||
qb.alias,
|
||||
isRelation,
|
||||
isVirtualProperty,
|
||||
isEmbeded,
|
||||
virtualQuery
|
||||
)
|
||||
const condition: WherePredicateOperator = {
|
||||
operator: 'ilike',
|
||||
parameters: [alias, `:${column}`],
|
||||
|
Loading…
Reference in New Issue
Block a user