35

Is it possible to do multiple orderBy() columns?

knex
  .select()
  .table('products')
  .orderBy('id', 'asc')

The orderBy() chainable only takes a single column key and a sort value, but how can I order by multiple columns?

Ian Jones
  • 1,369
  • 1
  • 10
  • 15

5 Answers5

59

You can call .orderBy multiple times to order by multiple columns:

knex
  .select()
  .table('products')
  .orderBy('name', 'desc')
  .orderBy('id', 'asc')
Kevin
  • 3,771
  • 2
  • 31
  • 40
  • 1
    Thanks, but is there any way to apply that programatically? for an unknown number of orderBy fields? – Ian Jones Apr 03 '16 at 22:08
  • 5
    Just iterate over the list of columns and call `orderBy` for each. I'll save the basic JS as an exercise for you :) – Kevin Apr 04 '16 at 00:33
20

The original answer is technically correct, and useful, but my intention was to find a way to programatically apply the orderBy() function multiple times, here is the actual solution I went with for reference:

var sortArray = [
  {'field': 'title', 'direction': 'asc'}, 
  {'field': 'id', 'direction': 'desc'}
];

knex
  .select()
  .table('products')
  .modify(function(queryBuilder) {
    _.each(sortArray, function(sort) {
      queryBuilder.orderBy(sort.field, sort.direction);
    });
  })

Knex offers a modify function which allows the queryBuilder to be operated on directly. An array iterator then calls orderBy() multiple times.

Ian Jones
  • 1,369
  • 1
  • 10
  • 15
  • I think the original answer was working as you intended too. For instance: let query = knex .select() .table('products'); _.each(sortArray, function(sort) { query.orderBy(sort.field, sort.direction); }); – nembleton Jul 12 '17 at 07:49
16

The Knex orderBy function also receives an array:

knex('users').orderBy(['email', 'age', 'name'])

or

knex('users').orderBy(['email', { column: 'age', order: 'desc' }])

or

knex('users').orderBy([{ column: 'email' }, { column: 'age', order: 'desc' }])
Rafael Zeffa
  • 2,334
  • 22
  • 20
4

You can use the following solution to solve your problem:

const builder = knex.table('products');

sortArray.forEach(
  ({ field, direction }) => builder.orderBy(field, direction)
);
Grant Miller
  • 27,532
  • 16
  • 147
  • 165
mabulu
  • 274
  • 2
  • 5
0

orderBy accepts an array of type:

[
  {column: 'id', order: 'asc'},
  {column: 'name', order: 'desc'},
  {column: 'created_at', order: 'desc'},
]

i have a function that takes a param from the request:

sort=id,name,-created_at

and builds an array that is passed to the queryBuilder

columns is an array with the accepted values of table columns

sort(model, sorts, columns) {

    let confirmed = true;

    sorts = sorts.split(',')
    sorts.forEach((sort: string) => {
        sort      = sort.replace('-', '')
        sort      = sort.replace(' ', '')
        confirmed = columns.includes(sort)
        if (!confirmed) {
            let index = sorts.indexOf(sort)
            sorts.splice(index, 1)
        }
    })

    let sortsArr = [];

    sorts.forEach((sort) => {
        if (sort.startsWith('-')) {
            sort = sort.replace('-', '')
            sortsArr.push({column: model.tableName + '.' + sort, order: 'desc'})
        } else {
            sortsArr.push({column: model.tableName + '.' + sort, order: 'asc'})
        }
    })
    return sortsArr;
}

and then use it like this in the query

const sortsArr = sort(model, sorts, model.columns);

knex('users').orderBy(sortsArr)
M-Raw
  • 779
  • 4
  • 10