Skip to content

Commit

Permalink
Merge pull request #146 from nestjsx/feature/nested-sort
Browse files Browse the repository at this point in the history
support nested sort
  • Loading branch information
michaelyali committed Jul 19, 2019
2 parents a3bcfa4 + 4e1cad1 commit d27a1d6
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 25 deletions.
55 changes: 31 additions & 24 deletions packages/crud-typeorm/src/typeorm-crud.service.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { Repository, ObjectLiteral, SelectQueryBuilder, Brackets } from 'typeorm';
import { RelationMetadata } from 'typeorm/metadata/RelationMetadata';
import { plainToClass } from 'class-transformer';
import { ClassType } from 'class-transformer/ClassTransformer';
import {
CreateManyDto,
CrudRequest,
CrudRequestOptions,
CreateManyDto,
QueryOptions,
JoinOptions,
GetManyDefaultResponse,
JoinOptions,
QueryOptions,
} from '@nestjsx/crud';
import { CrudService } from '@nestjsx/crud/lib/services';
import {
QueryJoin,
ParsedRequestParams,
QueryFilter,
QueryJoin,
QuerySort,
ParsedRequestParams,
} from '@nestjsx/crud-request';
import { isArrayFull, isObject, hasLength, objKeys, isUndefined } from '@nestjsx/util';
import { CrudService } from '@nestjsx/crud/lib/services';
import { hasLength, isArrayFull, isObject, isUndefined, objKeys } from '@nestjsx/util';
import { plainToClass } from 'class-transformer';
import { ClassType } from 'class-transformer/ClassTransformer';
import { Brackets, ObjectLiteral, Repository, SelectQueryBuilder } from 'typeorm';
import { RelationMetadata } from 'typeorm/metadata/RelationMetadata';

export class TypeOrmCrudService<T> extends CrudService<T> {
private entityColumns: string[];
Expand Down Expand Up @@ -270,7 +270,7 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
const allowedJoins = objKeys(joinOptions);

if (hasLength(allowedJoins)) {
let eagerJoins: any = {};
const eagerJoins: any = {};

for (let i = 0; i < allowedJoins.length; i++) {
/* istanbul ignore else */
Expand Down Expand Up @@ -405,16 +405,9 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
private validateHasColumn(column: string) {
if (column.indexOf('.') !== -1) {
const nests = column.split('.');

if (nests.length > 2) {
this.throwBadRequestException(
'Too many nested levels! ' +
`Usage: '[join=<other-relation>&]join=[<other-relation>.]<relation>&filter=<relation>.<field>||op||val'`,
);
}

let relation;
[relation, column] = nests;
column = nests[nests.length - 1];
relation = nests.slice(0, nests.length - 1).join('.');

if (!this.hasRelation(relation)) {
this.throwBadRequestException(`Invalid relation name '${relation}'`);
Expand Down Expand Up @@ -530,6 +523,7 @@ export class TypeOrmCrudService<T> extends CrudService<T> {

return true;
}

private setAndWhere(cond: QueryFilter, i: any, builder: SelectQueryBuilder<T>) {
this.validateHasColumn(cond.field);
const { str, params } = this.mapOperatorsToQuery(cond, `andWhere${i}`);
Expand Down Expand Up @@ -594,12 +588,26 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
? this.mapSort(options.sort)
: {};
}

private getFieldWithAlias(field: string) {
const cols = field.split('.');
// relation is alias
switch (cols.length) {
case 1:
return `${this.alias}.${field}`;
case 2:
return field;
default:
return cols.slice(cols.length - 2, cols.length).join('.');
}
}

private mapSort(sort: QuerySort[]) {
const params: ObjectLiteral = {};

for (let i = 0; i < sort.length; i++) {
this.validateHasColumn(sort[i].field);
params[`${this.alias}.${sort[i].field}`] = sort[i].order;
params[this.getFieldWithAlias(sort[i].field)] = sort[i].order;
}

return params;
Expand All @@ -609,8 +617,7 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
cond: QueryFilter,
param: any,
): { str: string; params: ObjectLiteral } {
const field =
cond.field.indexOf('.') === -1 ? `${this.alias}.${cond.field}` : cond.field;
const field = this.getFieldWithAlias(cond.field);
let str: string;
let params: ObjectLiteral;

Expand Down
79 changes: 79 additions & 0 deletions packages/crud-typeorm/test/1.query-params.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,85 @@ describe('#crud-typeorm', () => {
done();
});
});

it('should return joined entity, 2', (done) => {
const query = qb
.setFilter({ field: 'company.projects.id', operator: 'notnull' })
.setJoin({ field: 'company' })
.setJoin({ field: 'company.projects' })
.query();
return request(server)
.get('/users/1')
.query(query)
.end((_, res) => {
expect(res.status).toBe(200);
expect(res.body.company).toBeDefined();
expect(res.body.company.projects).toBeDefined();
done();
});
});
});

describe('#sort', () => {
it('should sort by field', async () => {
const query = qb.sortBy({ field: 'id', order: 'DESC' }).query();
const res = await request(server)
.get('/users')
.query(query)
.expect(200);
expect(res.body[1].id).toBeLessThan(res.body[0].id);
});

it('should sort by nested field, 1', async () => {
const query = qb
.setFilter({ field: 'company.id', operator: 'notnull' })
.setJoin({ field: 'company' })
.sortBy({ field: 'company.id', order: 'DESC' })
.query();
const res = await request(server)
.get('/users')
.query(query)
.expect(200);
expect(res.body[res.body.length - 1].company.id).toBeLessThan(
res.body[0].company.id,
);
});

it('should sort by nested field, 2', async () => {
const query = qb
.setFilter({ field: 'id', operator: 'eq', value: 1 })
.setFilter({ field: 'company.id', operator: 'notnull' })
.setFilter({ field: 'projects.id', operator: 'notnull' })
.setJoin({ field: 'company' })
.setJoin({ field: 'company.projects' })
.sortBy({ field: 'projects.id', order: 'DESC' })
.query();
const res = await request(server)
.get('/users')
.query(query)
.expect(200);
expect(res.body[0].company.projects[1].id).toBeLessThan(
res.body[0].company.projects[0].id,
);
});

it('should sort by nested field, 3', async () => {
const query = qb
.setFilter({ field: 'id', operator: 'eq', value: 1 })
.setFilter({ field: 'company.id', operator: 'notnull' })
.setFilter({ field: 'projects.id', operator: 'notnull' })
.setJoin({ field: 'company' })
.setJoin({ field: 'company.projects' })
.sortBy({ field: 'company.projects.id', order: 'DESC' })
.query();
const res = await request(server)
.get('/users')
.query(query)
.expect(200);
expect(res.body[0].company.projects[1].id).toBeLessThan(
res.body[0].company.projects[0].id,
);
});
});
});
});
5 changes: 4 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
"object-literal-sort-keys": false,
"member-access": false,
"no-implicit-dependencies": false,
"member-ordering": false
"member-ordering": false,
"prefer-for-of": false,
"no-submodule-imports": false,
"interface-name": false
},
"rulesDirectory": []
}

0 comments on commit d27a1d6

Please sign in to comment.