Skip to content

Commit 0950b94

Browse files
authored
feat: add support of array of tuples order format (#973). Thanks to @RusovDmitriy
1 parent 01759b3 commit 0950b94

File tree

3 files changed

+66
-6
lines changed

3 files changed

+66
-6
lines changed

docs/Cube.js-Backend/Query-Format.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,22 @@ If the `order` property is not specified in the query, Cube.js sorts results by
6666
- The first measure, descending. If no measure exists...
6767
- The first dimension, ascending.
6868

69+
### Аlternative order format
70+
71+
Also you can control the ordering of the `order` specification, Cube.js support alternative order format - array of tuples:
72+
73+
```js
74+
{
75+
...,
76+
order: [
77+
['Stories.time', 'asc'],
78+
['Stories.count', 'asc']
79+
]
80+
},
81+
...
82+
}
83+
```
84+
6985
## Filters Format
7086

7187
A filter is a Javascript object with the following properties:

packages/cubejs-api-gateway/index.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,10 @@ const querySchema = Joi.object().keys({
146146
Joi.string()
147147
]
148148
})),
149-
order: Joi.object().pattern(id, Joi.valid('asc', 'desc')),
149+
order: Joi.alternatives(
150+
Joi.object().pattern(id, Joi.valid('asc', 'desc')),
151+
Joi.array().items(Joi.array().min(2).ordered(id, Joi.valid('asc', 'desc')))
152+
),
150153
segments: Joi.array().items(id),
151154
timezone: Joi.string(),
152155
limit: Joi.number().integer().min(1).max(50000),
@@ -155,6 +158,20 @@ const querySchema = Joi.object().keys({
155158
ungrouped: Joi.boolean()
156159
});
157160

161+
const normalizeQueryOrder = order => {
162+
let result = [];
163+
const normalizeOrderItem = (k, direction) => ({
164+
id: k,
165+
desc: direction === 'desc'
166+
});
167+
if (order) {
168+
result = Array.isArray(order) ?
169+
order.map(([k, direction]) => normalizeOrderItem(k, direction)) :
170+
Object.keys(order).map(k => normalizeOrderItem(k, order[k]));
171+
}
172+
return result;
173+
};
174+
158175
const DateRegex = /^\d\d\d\d-\d\d-\d\d$/;
159176

160177
const normalizeQuery = (query) => {
@@ -209,15 +226,11 @@ const normalizeQuery = (query) => {
209226
granularity: d.split('.')[2]
210227
}));
211228
const timezone = query.timezone || 'UTC';
212-
const order = query.order && Object.keys(query.order).map(k => ({
213-
id: k,
214-
desc: query.order[k] === 'desc'
215-
}));
216229
return {
217230
...query,
218231
rowLimit: query.rowLimit || query.limit,
219232
timezone,
220-
order,
233+
order: normalizeQueryOrder(query.order),
221234
filters: (query.filters || []).map(f => (
222235
{
223236
...f,

packages/cubejs-api-gateway/index.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,35 @@ describe(`API Gateway`, () => {
9090
"2020-01-01T23:59:59.999"
9191
]);
9292
});
93+
94+
test(`order support object format`, async () => {
95+
const query = {
96+
measures: ["Foo.bar"],
97+
order: {
98+
'Foo.bar': 'asc'
99+
},
100+
};
101+
const res = await request(app)
102+
.get(`/cubejs-api/v1/load?query=${JSON.stringify(query)}`)
103+
.set('Authorization', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.t-IDcSemACt8x4iTMCda8Yhe3iZaWbvV5XKSTbuAn0M')
104+
.expect(200);
105+
106+
expect(res.body.query.order).toStrictEqual([{ id: 'Foo.bar', desc: false }]);
107+
});
108+
109+
test(`order support array of tuples`, async () => {
110+
const query = {
111+
measures: ["Foo.bar"],
112+
order: [
113+
['Foo.bar', 'asc'],
114+
['Foo.foo', 'desc']
115+
],
116+
};
117+
const res = await request(app)
118+
.get(`/cubejs-api/v1/load?query=${JSON.stringify(query)}`)
119+
.set('Authorization', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.t-IDcSemACt8x4iTMCda8Yhe3iZaWbvV5XKSTbuAn0M')
120+
.expect(200);
121+
122+
expect(res.body.query.order).toStrictEqual([{ id: 'Foo.bar', desc: false }, { id: 'Foo.foo', desc: true }]);
123+
});
93124
});

0 commit comments

Comments
 (0)