Sling Academy
Home/Node.js/Mongoose: How to sort results by multiple fields

Mongoose: How to sort results by multiple fields

Last updated: December 30, 2023

Introduction

Sorting results retrieved from a MongoDB database is a common requirement while developing applications. With Mongoose, a popular ODM (Object Data Modeling) library for MongoDB and Node.js, you can perform complex sorting on multiple fields with ease. This guide will walk you through the process of sorting query results by multiple fields using various sorting patterns and techniques.

Basic Sorting

Before delving into multi-field sorting, let’s start with basic sorting syntax in Mongoose. The .sort() function allows you to sort the results of a query by any field(s) in ascending or descending order. Ascending order is the default, but you can specify descending order by prefixing the field name with a minus (‘-‘).

const Person = mongoose.model('Person', personSchema);
async function getSortedPeople() {
  const people = await Person.find().sort('name'); // Ascending by 'name'
  return people;
}

async function getSortedPeopleDesc() {
  const people = await Person.find().sort('-name'); // Descending by 'name'
  return people;
}

Sorting by Multiple Fields

Sorting by multiple fields is where things get interesting. You provide a sort object with keys being the field names and values indicating the order of sorting. This way, you can indicate the prioritization of each field during sorting.

async function getSortedPeopleByAgeAndName() {
  const people = await Person.find().sort({ age: 1, name: -1 });
  return people;
}

Advanced Sorting Patterns

When combining sorting with other query characteristics such as pagination, it is essential to implement consistent and efficient sorting. A common pattern is to apply sorting along with a cursor-based pagination.

async function getPaginatedAndSortedPeople(startAfter, limit, sortOrder) {
  const query = Person.find({ _id: { $gt: startAfter } }).limit(limit);
  const sortedPeople = await query.sort(sortOrder);
  return sortedPeople;
}

// Usage with sort order
const sortOrder = { age: 1, createdAt: 1, name: -1 };
getPaginatedAndSortedPeople(last_person_id, 10, sortOrder);

Sorting and Indexing

Sorting becomes more powerful and efficient when combined with indexes. Without proper indexing, the database must perform a collection scan every time, which can be inefficient for large datasets. Here, we ensure fields that are frequently sorted are indexed.

personSchema.index({ age: 1, name: 1 });

Use Case Examples

Next, let’s consider some practical use cases showcasing the benefits of performing nuanced multi-field sorts within an application context.

// Sort 'Person' entries by 'location' (ascending), then by 'registrationDate' (descending)
async function getPeopleByLocation(location) {
  const people = await Person.find({ location }).sort({ registrationDate: -1 });
  return people;
}

Sorting with Aggregations

In addition to basic queries, Mongoose aggregation pipeline provides powerful data processing capabilities. The $sort stage allows us to sort the documents processed by the preceding stages by one or more fields.

// Sorting within an aggregation pipeline
async function getSummaryByAge() {
  const summary = await Person.aggregate([
    { $group: { _id: '$age', count: { $sum: 1 } } },
    { $sort: { count: -1, _id: 1 } }
  ]);
  return summary;
}

Handling Sort Exceptions

When implementing sorting it’s also important to plan for cases where sort might fail or where the specified fields may not exist. Understanding the Error Handling is crucial.

// Error handling for sorting
async function getSortedPeopleSafe() {
  try {
    const people = await Person.find().sort({ nonExistentField: 1, name: -1 });
  } catch (error) {
    // Handle the error appropriately
  }
}

Conclusion

In this guide, we explored the robust and flexible sorting capabilities of Mongoose, focusing on sorting by multiple fields for Node.js applications. Through sorting query results, combined with indexing and understand errors, developers can deliver faster, more intuitive data retrieval experiences.

Next Article: Mongoose: How to filter documents by multiple fields

Previous Article: How to query distinct values in Mongoose

Series: Mongoose.js Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates