Skip to content

Commit

Permalink
feat(core): add support for ordering by NULLS (#677)
Browse files Browse the repository at this point in the history
Closes #675
  • Loading branch information
mcasarrubios authored and B4nan committed Aug 9, 2020
1 parent bbe1b80 commit 74ee0cb
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 6 deletions.
8 changes: 8 additions & 0 deletions packages/core/src/enums.ts
Expand Up @@ -19,9 +19,17 @@ export enum QueryOperator {

export enum QueryOrder {
ASC = 'ASC',
ASC_NULLS_LAST = 'ASC NULLS LAST',
ASC_NULLS_FIRST = 'ASC NULLS FIRST',
DESC = 'DESC',
DESC_NULLS_LAST = 'DESC NULLS LAST',
DESC_NULLS_FIRST = 'DESC NULLS FIRST',
asc = 'asc',
asc_nulls_last = 'asc nulls last',
asc_nulls_first = 'asc nulls first',
desc = 'desc',
desc_nulls_last = 'desc nulls last',
desc_nulls_first = 'desc nulls first',
}

export enum QueryOrderNumeric {
Expand Down
2 changes: 1 addition & 1 deletion packages/knex/src/query/QueryBuilder.ts
Expand Up @@ -228,7 +228,7 @@ export class QueryBuilder<T extends AnyEntity<T> = AnyEntity> {
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._cond, qb), this._cond);
Utils.runIfNotEmpty(() => qb.groupBy(this.prepareFields(this._groupBy, 'groupBy')), this._groupBy);
Utils.runIfNotEmpty(() => this.helper.appendQueryCondition(this.type, this._having, qb, undefined, 'having'), this._having);
Utils.runIfNotEmpty(() => qb.orderBy(this.helper.getQueryOrder(this.type, this._orderBy as FlatQueryOrderMap, this._populateMap)), this._orderBy);
Utils.runIfNotEmpty(() => qb.orderByRaw(this.helper.getQueryOrder(this.type, this._orderBy as FlatQueryOrderMap, this._populateMap)), this._orderBy);
Utils.runIfNotEmpty(() => qb.limit(this._limit!), this._limit);
Utils.runIfNotEmpty(() => qb.offset(this._offset!), this._offset);

Expand Down
11 changes: 6 additions & 5 deletions packages/knex/src/query/QueryBuilderHelper.ts
Expand Up @@ -382,9 +382,8 @@ export class QueryBuilderHelper {
}
}

getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): { column: string; order: string }[] {
const ret: { column: string; order: string }[] = [];

getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap, populate: Dictionary<string>): string {
const ret: string[] = [];
Object.keys(orderBy).forEach(k => {
// eslint-disable-next-line prefer-const
let [alias, field] = this.splitField(k);
Expand All @@ -394,12 +393,14 @@ export class QueryBuilderHelper {
const prop = this.getProperty(f, alias);
const noPrefix = prop && prop.persist === false;
const order = Utils.isNumber<QueryOrderNumeric>(direction) ? QueryOrderNumeric[direction] : direction;
const column = this.mapper(noPrefix ? f : `${alias}.${f}`, type);
const rawColumn = column.split('.').map(e => this.knex.ref(e)).join('.');

ret.push({ column: this.mapper(noPrefix ? f : `${alias}.${f}`, type), order: order.toLowerCase() });
ret.push(`${rawColumn} ${order.toLowerCase()}`);
});
});

return ret;
return ret.join(', ');
}

finalize(type: QueryType, qb: KnexQueryBuilder, meta?: EntityMetadata): void {
Expand Down
12 changes: 12 additions & 0 deletions tests/QueryBuilder.test.ts
Expand Up @@ -1378,6 +1378,18 @@ describe('QueryBuilder', () => {
expect(qb4.getParams()).toEqual([1]);
});

test('order by asc nulls first', async () => {
const qb = orm.em.createQueryBuilder(Publisher2);
qb.select('*').orderBy({ name: QueryOrder.ASC_NULLS_FIRST });
expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` order by `e0`.`name` asc nulls first');
});

test('order by nulls last', async () => {
const qb = orm.em.createQueryBuilder(Publisher2);
qb.select('*').orderBy({ name: QueryOrder.DESC_NULLS_LAST, type: QueryOrder.ASC_NULLS_LAST });
expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` order by `e0`.`name` desc nulls last, `e0`.`type` asc nulls last');
});

afterAll(async () => orm.close(true));

});

0 comments on commit 74ee0cb

Please sign in to comment.