Skip to content

Commit

Permalink
fix(query-typeorm): Escape the aggregated group by and sort by
Browse files Browse the repository at this point in the history
Fixes the "unknown column" error of Postgres
  • Loading branch information
TriPSs committed Feb 1, 2023
1 parent 9c3be40 commit 787889e
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 22 deletions.
2 changes: 2 additions & 0 deletions examples/helpers/typeorm.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const typeormPostgresOptions = (
autoLoadEntities: true,
synchronize: true,
dropSchema: true,
logger: 'simple-console',
logging: ['error'],
...overrides
} as TypeOrmModuleOptions)

Expand Down
2 changes: 1 addition & 1 deletion examples/typeorm/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { TodoItemModule } from './todo-item/todo-item.module'
TypeOrmModule.forRoot(typeormOrmConfig('typeorm')),
GraphQLModule.forRoot({
driver: ApolloDriver,
autoSchemaFile: 'schema.gql',
autoSchemaFile: 'examples/typeorm/schema.gql',
context: ({ req }: { req: { headers: Record<string, string> } }): GqlContext => ({ request: req }),
formatError: formatGraphqlError
}),
Expand Down
26 changes: 18 additions & 8 deletions packages/query-typeorm/src/query/filter-query.builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,21 +202,31 @@ export class FilterQueryBuilder<Entity> {
}, qb)
}

public applyAggregateGroupBy<T extends Groupable<Entity>>(qb: T, groupBy?: AggregateQueryField<Entity>[], alias?: string): T {
if (!groupBy) {
public applyAggregateGroupBy<T extends Groupable<Entity>>(
qb: T,
aggregatedGroupBy?: AggregateQueryField<Entity>[],
alias?: string
): T {
if (!aggregatedGroupBy) {
return qb
}
return groupBy.reduce((prevQb, { field }) => {
return prevQb.addGroupBy(AggregateBuilder.getGroupByAlias(field))

return aggregatedGroupBy.reduce((prevQb, aggregatedField) => {
return prevQb.addGroupBy(prevQb.escape(AggregateBuilder.getGroupByAlias(aggregatedField.field)))
}, qb)
}

public applyAggregateSorting<T extends Sortable<Entity>>(qb: T, groupBy?: AggregateQueryField<Entity>[], alias?: string): T {
if (!groupBy) {
public applyAggregateSorting<T extends Sortable<Entity>>(
qb: T,
aggregatedGroupBy?: AggregateQueryField<Entity>[],
alias?: string
): T {
if (!aggregatedGroupBy) {
return qb
}
return groupBy.reduce((prevQb, { field }) => {
return prevQb.addOrderBy(AggregateBuilder.getGroupByAlias(field), 'ASC')

return aggregatedGroupBy.reduce((prevQb, aggregatedField) => {
return prevQb.addOrderBy(prevQb.escape(AggregateBuilder.getGroupByAlias(aggregatedField.field)), 'ASC')
}, qb)
}

Expand Down
26 changes: 13 additions & 13 deletions packages/query-typeorm/src/services/typeorm-query.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class TypeOrmQueryService<Entity>
}

// eslint-disable-next-line @typescript-eslint/naming-convention
get EntityClass(): Class<Entity> {
public get EntityClass(): Class<Entity> {
return this.repo.target as Class<Entity>
}

Expand All @@ -77,17 +77,17 @@ export class TypeOrmQueryService<Entity>
* ```
* @param query - The Query used to filter, page, and sort rows.
*/
async query(query: Query<Entity>): Promise<Entity[]> {
public async query(query: Query<Entity>): Promise<Entity[]> {
return this.filterQueryBuilder.select(query).getMany()
}

async aggregate(filter: Filter<Entity>, aggregate: AggregateQuery<Entity>): Promise<AggregateResponse<Entity>[]> {
public async aggregate(filter: Filter<Entity>, aggregate: AggregateQuery<Entity>): Promise<AggregateResponse<Entity>[]> {
return AggregateBuilder.asyncConvertToAggregateResponse(
this.filterQueryBuilder.aggregate({ filter }, aggregate).getRawMany<Record<string, unknown>>()
)
}

async count(filter: Filter<Entity>): Promise<number> {
public async count(filter: Filter<Entity>): Promise<number> {
return this.filterQueryBuilder.select({ filter }).getCount()
}

Expand All @@ -101,7 +101,7 @@ export class TypeOrmQueryService<Entity>
* @param id - The id of the record to find.
* @param opts
*/
async findById(id: string | number, opts?: FindByIdOptions<Entity>): Promise<Entity | undefined> {
public async findById(id: string | number, opts?: FindByIdOptions<Entity>): Promise<Entity | undefined> {
const qb = this.filterQueryBuilder.selectById(id, opts ?? {})
if (opts?.withDeleted) {
qb.withDeleted()
Expand All @@ -124,7 +124,7 @@ export class TypeOrmQueryService<Entity>
* @param id - The id of the record to find.
* @param opts
*/
async getById(id: string | number, opts?: GetByIdOptions<Entity>): Promise<Entity> {
public async getById(id: string | number, opts?: GetByIdOptions<Entity>): Promise<Entity> {
const entity = await this.findById(id, opts)
if (!entity) {
throw new NotFoundException(`Unable to find ${this.EntityClass.name} with id: ${id}`)
Expand All @@ -141,7 +141,7 @@ export class TypeOrmQueryService<Entity>
* ```
* @param record - The entity to create.
*/
async createOne(record: DeepPartial<Entity>): Promise<Entity> {
public async createOne(record: DeepPartial<Entity>): Promise<Entity> {
const entity = await this.ensureIsEntityAndDoesNotExist(record)

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand All @@ -161,7 +161,7 @@ export class TypeOrmQueryService<Entity>
* ```
* @param records - The entities to create.
*/
async createMany(records: DeepPartial<Entity>[]): Promise<Entity[]> {
public async createMany(records: DeepPartial<Entity>[]): Promise<Entity[]> {
const entities = await Promise.all(records.map((r) => this.ensureIsEntityAndDoesNotExist(r)))
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
Expand Down Expand Up @@ -200,7 +200,7 @@ export class TypeOrmQueryService<Entity>
* @param update - A `Partial` of entity with the fields to update
* @param filter - A Filter used to find the records to update
*/
async updateMany(update: DeepPartial<Entity>, filter: Filter<Entity>): Promise<UpdateManyResponse> {
public async updateMany(update: DeepPartial<Entity>, filter: Filter<Entity>): Promise<UpdateManyResponse> {
this.ensureIdIsNotPresent(update)
let updateResult: UpdateResult

Expand Down Expand Up @@ -240,7 +240,7 @@ export class TypeOrmQueryService<Entity>
* @param filter Additional filter to use when finding the entity to delete.
* @param opts - Additional options.
*/
async deleteOne(id: string | number, opts?: DeleteOneOptions<Entity>): Promise<Entity> {
public async deleteOne(id: string | number, opts?: DeleteOneOptions<Entity>): Promise<Entity> {
const entity = await this.getById(id, opts)
if (this.useSoftDelete || opts?.useSoftDelete) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
Expand All @@ -265,7 +265,7 @@ export class TypeOrmQueryService<Entity>
* @param filter - A `Filter` to find records to delete.
* @param opts - Additional delete many options
*/
async deleteMany(filter: Filter<Entity>, opts?: DeleteManyOptions<Entity>): Promise<DeleteManyResponse> {
public async deleteMany(filter: Filter<Entity>, opts?: DeleteManyOptions<Entity>): Promise<DeleteManyResponse> {
let deleteResult = {} as DeleteResult

if (this.filterQueryBuilder.filterHasRelations(filter)) {
Expand Down Expand Up @@ -306,7 +306,7 @@ export class TypeOrmQueryService<Entity>
* @param id - The `id` of the entity to restore.
* @param opts Additional filter to use when finding the entity to restore.
*/
async restoreOne(id: string | number, opts?: Filterable<Entity>): Promise<Entity> {
public async restoreOne(id: string | number, opts?: Filterable<Entity>): Promise<Entity> {
this.ensureSoftDeleteEnabled()
await this.repo.restore(id)
return this.getById(id, opts)
Expand All @@ -325,7 +325,7 @@ export class TypeOrmQueryService<Entity>
*
* @param filter - A `Filter` to find records to delete.
*/
async restoreMany(filter: Filter<Entity>): Promise<UpdateManyResponse> {
public async restoreMany(filter: Filter<Entity>): Promise<UpdateManyResponse> {
this.ensureSoftDeleteEnabled()
const result = await this.filterQueryBuilder.softDelete({ filter }).restore().execute()
return { updatedCount: result.affected || 0 }
Expand Down

0 comments on commit 787889e

Please sign in to comment.