Skip to content

Commit

Permalink
Add sorting by multiple criteria option
Browse files Browse the repository at this point in the history
Closes #29
  • Loading branch information
hagopj13 committed Sep 28, 2020
1 parent 9a3e435 commit 677ee12
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 9 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,14 @@ The `options` param can have the following (optional) fields:

```javascript
const options = {
sortBy: 'name:desc',
limit: 5,
page: 2,
sortBy: 'name:desc', // sort order
limit: 5, // maximum results per page
page: 2, // page number
};
```

The plugin also supports sorting by multiple criteria (separated by a comma): `sortBy: name:desc,role:asc`

The `paginate` method returns a Promise, which fulfills with an object having the following properties:

```json
Expand Down
13 changes: 9 additions & 4 deletions src/models/plugins/paginate.plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@ const paginate = (schema) => {
* Query for documents with pagination
* @param {Object} [filter] - Mongo filter
* @param {Object} [options] - Query options
* @param {string} [options.sortBy] - Sort option in the format: sortField:(desc|asc)
* @param {string} [options.sortBy] - Sorting criteria using the format: sortField:(desc|asc). Multiple sorting criteria should be separated by commas (,)
* @param {number} [options.limit] - Maximum number of results per page (default = 10)
* @param {number} [options.page] - Current page (default = 1)
* @returns {Promise<QueryResult>}
*/
schema.statics.paginate = async function (filter, options) {
const sort = {};
let sort = '';
if (options.sortBy) {
const parts = options.sortBy.split(':');
sort[parts[0]] = parts[1] === 'desc' ? -1 : 1;
const sortingCriteria = [];
options.sortBy.split(',').forEach((sortOption) => {
const [key, order] = sortOption.split(':');
sortingCriteria.push((order === 'desc' ? '-' : '') + key);
});
sort = sortingCriteria.join(' ');
}

const limit = options.limit && parseInt(options.limit, 10) > 0 ? parseInt(options.limit, 10) : 10;
const page = options.page && parseInt(options.page, 10) > 0 ? parseInt(options.page, 10) : 1;
const skip = (page - 1) * limit;
Expand Down
38 changes: 36 additions & 2 deletions tests/integration/user.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ describe('User routes', () => {
expect(res.body.results[1].id).toBe(userTwo._id.toHexString());
});

test('should correctly sort returned array if descending sort param is specified', async () => {
test('should correctly sort the returned array if descending sort param is specified', async () => {
await insertUsers([userOne, userTwo, admin]);

const res = await request(app)
Expand All @@ -242,7 +242,7 @@ describe('User routes', () => {
expect(res.body.results[2].id).toBe(admin._id.toHexString());
});

test('should correctly sort returned array if ascending sort param is specified', async () => {
test('should correctly sort the returned array if ascending sort param is specified', async () => {
await insertUsers([userOne, userTwo, admin]);

const res = await request(app)
Expand All @@ -265,6 +265,40 @@ describe('User routes', () => {
expect(res.body.results[2].id).toBe(userTwo._id.toHexString());
});

test('should correctly sort the returned array if multiple sorting criteria are specified', async () => {
await insertUsers([userOne, userTwo, admin]);

const res = await request(app)
.get('/v1/users')
.set('Authorization', `Bearer ${adminAccessToken}`)
.query({ sortBy: 'role:desc,name:asc' })
.send()
.expect(httpStatus.OK);

expect(res.body).toEqual({
results: expect.any(Array),
page: 1,
limit: 10,
totalPages: 1,
totalResults: 3,
});
expect(res.body.results).toHaveLength(3);

const expectedOrder = [userOne, userTwo, admin].sort((a, b) => {
if (a.role < b.role) {
return 1;
}
if (a.role > b.role) {
return -1;
}
return a.name < b.name ? -1 : 1;
});

expectedOrder.forEach((user, index) => {
expect(res.body.results[index].id).toBe(user._id.toHexString());
});
});

test('should limit returned array if limit param is specified', async () => {
await insertUsers([userOne, userTwo, admin]);

Expand Down

1 comment on commit 677ee12

@kvlknctk
Copy link

Choose a reason for hiding this comment

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

You are awesome.

Please sign in to comment.