Skip to content
This repository was archived by the owner on Jun 14, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,5 +180,6 @@ In addition to the filter parameters, there is a set of query parameters that ha
| `eager=[children, parent.movies]` | Which relations to fetch eagerly for the result models. An objection.js relation expression. |
| `orderBy=firstName` | Sort the result by certain property. |
| `orderByDesc=firstName` | Sort the result by certain property in descending order. |
| `orderByAsc=group\|lastName` | Sort the result by more than one property in ascending order. |

@kibertoad kibertoad Apr 1, 2020

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems not very RESTful. Can you adjust the param structure to follow either
?foo=bar&foo=qux
or
?foo=bar,qux
, as suggested in https://medium.com/raml-api/arrays-in-query-params-33189628fa68 ?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same should be documented for desc sorting.

| `rangeStart=10` | The start of the result range (inclusive). The result will be `{total: 12343, results: [ ... ]}`. |
| `rangeEnd=50` | The end of the result range (inclusive). The result will be `{total: 12343, results: [ ... ]}`. |
30 changes: 15 additions & 15 deletions lib/FindQueryBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,28 +449,28 @@ class FindQueryBuilder {

_.each(params, param => {
const orderType = param.specialParameter;
let dir = 'asc';

if (orderType && orderType.indexOf('orderBy') !== -1) {
const propertyRef = param.propertyRefs[0];

let dir = 'asc';
if (orderType === 'orderByDesc') {
dir = 'desc';
}

const rel = propertyRef.relation;
if (rel) {
if (!rel.isOneToOne()) {
utils.throwError(
"Can only order by model's own properties and by BelongsToOneRelation relations' properties"
);
_.each(param.propertyRefs, propertyRef => {
const rel = propertyRef.relation;
if (rel) {
if (!rel.isOneToOne()) {
utils.throwError(
"Can only order by model's own properties and by BelongsToOneRelation relations' properties"
);
}
const columnNameAlias = rel.name + _.capitalize(propertyRef.propertyName);
builder.select(propertyRef.fullColumnName() + ' as ' + columnNameAlias);
builder.orderBy(columnNameAlias, dir);
} else {
builder.orderBy(propertyRef.columnName, dir);
}
const columnNameAlias = rel.name + _.capitalize(propertyRef.propertyName);
builder.select(propertyRef.fullColumnName() + ' as ' + columnNameAlias);
builder.orderBy(columnNameAlias, dir);
} else {
builder.orderBy(propertyRef.columnName, dir);
}
});
}
});
}
Expand Down
4 changes: 3 additions & 1 deletion lib/QueryParameter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

const _ = require('lodash');
const utils = require('./utils');
const filters = require('./filters');

Expand Down Expand Up @@ -68,7 +69,8 @@ class QueryParameter {
}

_parseOrderBy(value, key, builder) {
this.propertyRefs.push(builder._parsePropertyRef(value));
let parsedRefs = builder._parsePropertyRefs(value.split('|'));
_.each(parsedRefs, parsedRef => this.propertyRefs.push(parsedRef));
}

_parseFilter(value, key, builder) {
Expand Down
72 changes: 59 additions & 13 deletions tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,22 @@ describe('integration tests', () => {
* Each person is an actor in 10 Movies `M00`, `M01`, `M02`, ...
* First person has movies 0 - 9, second 10 - 19 etc.
*
* name | parent | pets | movies
* --------+---------+-----------+----------
* F00 L09 | null | P00 - P09 | M99 - M90
* F01 L08 | F00 L09 | P10 - P19 | M89 - M80
* F02 L07 | F01 L08 | P20 - P29 | M79 - M79
* F03 L06 | F02 L07 | P30 - P39 | M69 - M60
* F04 L05 | F03 L06 | P40 - P49 | M59 - M50
* F05 L04 | F04 L05 | P50 - P59 | M49 - M40
* F06 L03 | F05 L04 | P60 - P69 | M39 - M30
* F07 L02 | F06 L03 | P70 - P79 | M29 - M20
* F08 L01 | F07 L02 | P80 - P89 | M19 - M10
* F09 L00 | F08 L01 | P90 - P99 | M09 - M00
* Each person is in one of three groups, `0`, `1` or `2`.
* First, second and third persons are in groups `0`, `1` and `2`. Fourth person is in group `0`
* and this pattern repeats.
*
* name | parent | pets | movies | group
* --------+---------+-----------+-----------+------
* F00 L09 | null | P00 - P09 | M99 - M90 | 0
* F01 L08 | F00 L09 | P10 - P19 | M89 - M80 | 1
* F02 L07 | F01 L08 | P20 - P29 | M79 - M79 | 2
* F03 L06 | F02 L07 | P30 - P39 | M69 - M60 | 0
* F04 L05 | F03 L06 | P40 - P49 | M59 - M50 | 1
* F05 L04 | F04 L05 | P50 - P59 | M49 - M40 | 2
* F06 L03 | F05 L04 | P60 - P69 | M39 - M30 | 0
* F07 L02 | F06 L03 | P70 - P79 | M29 - M20 | 1
* F08 L01 | F07 L02 | P80 - P89 | M19 - M10 | 2
* F09 L00 | F08 L01 | P90 - P99 | M09 - M00 | 0
*/
before(() => testUtils.insertData(session, { persons: 10, pets: 10, movies: 10 }));

Expand Down Expand Up @@ -105,7 +109,7 @@ describe('integration tests', () => {
'F09'
]);
});
})
});
});

describe('lt', function() {
Expand Down Expand Up @@ -397,6 +401,27 @@ describe('integration tests', () => {
expect(_.map(result, 'name')).to.contain('P00')
})
})

it('should order by multiple columns in ascending order', function() {
return objectionFind(Person)
.build({
orderByAsc: 'group|lastName'
})
.then(function(result) {
expect(_.invokeMap(result, 'fullName')).to.eql([
'F09 L00',
'F06 L03',
'F03 L06',
'F00 L09',
'F07 L02',
'F04 L05',
'F01 L08',
'F08 L01',
'F05 L04',
'F02 L07'
]);
});
});
});

describe('orderByDesc', function() {
Expand Down Expand Up @@ -438,6 +463,27 @@ describe('integration tests', () => {
expect(_.map(result, 'name')).to.contain('P00')
})
})

it('should order by multiple columns in descending order', function() {
return objectionFind(Person)
.build({
orderByDesc: 'group|lastName'
})
.then(function(result) {
expect(_.invokeMap(result, 'fullName')).to.eql([
'F02 L07',
'F05 L04',
'F08 L01',
'F01 L08',
'F04 L05',
'F07 L02',
'F00 L09',
'F03 L06',
'F06 L03',
'F09 L00'
]);
});
});
});
});

Expand Down
4 changes: 3 additions & 1 deletion tests/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = {
.index();
table.string('firstName');
table.string('lastName');
table.integer('group');
})
.createTableIfNotExists('Animal', function(table) {
table
Expand Down Expand Up @@ -110,6 +111,7 @@ module.exports = {
firstName: 'F' + zeroPad(p),
lastName: 'L' + zeroPad(P - p - 1),
age: p * 10,
group: p % 3,

pets: _.times(A, function(a) {
const id = p * A + a + 1;
Expand All @@ -132,7 +134,7 @@ module.exports = {
_.map(_.chunk(persons, C), function(personChunk) {
return session
.knex('Person')
.insert(pick(personChunk, ['id', 'firstName', 'lastName', 'age']));
.insert(pick(personChunk, ['id', 'firstName', 'lastName', 'age', 'group']));
})
)
.then(function() {
Expand Down