Skip to content

Commit

Permalink
feat(query-typeorm): Support virtual columns in sorting
Browse files Browse the repository at this point in the history
Fixes #67
  • Loading branch information
TriPSs committed Jan 11, 2024
1 parent a9f6ea9 commit cb997cf
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 56 deletions.
94 changes: 40 additions & 54 deletions examples/typeorm/e2e/todo-item.resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,60 +456,46 @@ describe('TodoItemResolver (typeorm - e2e)', () => {
expect(edges).toHaveLength(1)
}))

// it(`should allow sorting on a virtual column`, () =>
// request(app.getHttpServer())
// .post('/graphql')
// .send({
// operationName: null,
// variables: {},
// query: `{
// todoItems(sorting: [{field: subTasksCount, direction: DESC}]) {
// ${pageInfoField}
// ${edgeNodes(todoItemFields)}
// totalCount
// }
// }`
// })
// .expect(200)
// .then(({ body }) => {
// const { edges, pageInfo, totalCount }: CursorConnectionType<TodoItemDTO> = body.data.todoItems
// expect(pageInfo).toEqual({
// endCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6ImlkIiwidmFsdWUiOjF9XX0=',
// hasNextPage: false,
// hasPreviousPage: false,
// startCursor: 'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6ImlkIiwidmFsdWUiOjV9XX0='
// })
// expect(totalCount).toBe(5)
// expect(edges).toHaveLength(5)
// expect(edges.map((e) => e.node)).toEqual([
// {
// id: '5',
// title: 'How to create item With Sub Tasks',
// completed: false,
// description: null,
// age: expect.any(Number),
// subTasksCount: 3
// },
// {
// id: '4',
// title: 'Add Todo Item Resolver',
// completed: false,
// description: null,
// age: expect.any(Number),
// subTasksCount: 3
// },
// {
// id: '3',
// title: 'Create Entity Service',
// completed: false,
// description: null,
// age: expect.any(Number),
// subTasksCount: 3
// },
// { id: '2', title: 'Create Entity', completed: false, description: null, age: expect.any(Number), subTasksCount: 3 },
// { id: '1', title: 'Create Nest App', completed: true, description: null, age: expect.any(Number), subTasksCount: 3 }
// ])
// }))
it(`should allow sorting on a virtual column`, () =>
request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
// language=graphql
query: `{
todoItems(sorting: [{field: subTasksCount, direction: ASC}], paging: {first: 2}) {
${pageInfoField}
${edgeNodes(todoItemFields)}
totalCount
}
}`
})
.expect(200)
.then(({ body }) => {
const { edges, pageInfo, totalCount }: CursorConnectionType<TodoItemDTO> = body.data.todoItems
expect(pageInfo).toEqual({
endCursor:
'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN1YlRhc2tzQ291bnQiLCJ2YWx1ZSI6M30seyJmaWVsZCI6ImlkIiwidmFsdWUiOjF9XX0=',
hasNextPage: true,
hasPreviousPage: false,
startCursor:
'eyJ0eXBlIjoia2V5c2V0IiwiZmllbGRzIjpbeyJmaWVsZCI6InN1YlRhc2tzQ291bnQiLCJ2YWx1ZSI6Mn0seyJmaWVsZCI6ImlkIiwidmFsdWUiOjV9XX0='
})
expect(totalCount).toBe(5)
expect(edges).toHaveLength(2)
expect(edges.map((e) => e.node)).toEqual([
{
id: '5',
title: 'How to create item With Sub Tasks',
completed: false,
description: null,
age: expect.any(Number),
subTasksCount: 2
},
{ id: '1', title: 'Create Nest App', completed: true, description: null, age: expect.any(Number), subTasksCount: 3 }
])
}))

describe('paging', () => {
it(`should allow paging with the 'first' field`, () =>
Expand Down
15 changes: 13 additions & 2 deletions packages/query-typeorm/src/query/filter-query.builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,19 @@ export interface NestedRelationsAliased {
* Class that will convert a Query into a `typeorm` Query Builder.
*/
export class FilterQueryBuilder<Entity> {
private readonly virtualColumns: string[] = []

constructor(
readonly repo: Repository<Entity>,
readonly whereBuilder: WhereBuilder<Entity> = new WhereBuilder<Entity>(
new SQLComparisonBuilder<Entity>(SQLComparisonBuilder.DEFAULT_COMPARISON_MAP, repo)
),
readonly aggregateBuilder: AggregateBuilder<Entity> = new AggregateBuilder<Entity>(repo)
) {}
) {
this.virtualColumns = repo.metadata.columns
.filter(({ isVirtualProperty }) => isVirtualProperty)
.map(({ propertyName }) => propertyName)
}

/**
* Create a `typeorm` SelectQueryBuilder with `WHERE`, `ORDER BY` and `LIMIT/OFFSET` clauses.
Expand Down Expand Up @@ -212,7 +218,12 @@ export class FilterQueryBuilder<Entity> {
}

return sorts.reduce((prevQb, { field, direction, nulls }) => {
const col = alias ? `${alias}.${field as string}` : `${field as string}`
let col = alias ? `${alias}.${field as string}` : `${field as string}`

if (this.virtualColumns.includes(field as string)) {
col = prevQb.escape(alias ? `${alias}_${field as string}` : `${field as string}`)
}

return prevQb.addOrderBy(col, direction, nulls)
}, qb)
}
Expand Down

0 comments on commit cb997cf

Please sign in to comment.