Sling Academy
Home/Node.js/Mongoose: How to filter documents by multiple fields

Mongoose: How to filter documents by multiple fields

Last updated: December 30, 2023

Introduction

Mongoose is a powerful Object Data Modeling (ODM) library for MongoDB and Node.js. It provides a higher-level abstraction over MongoDB’s native operations. Filtering documents based on criteria is among the key features of any database system, and Mongoose offers versatile ways to accomplish this. In this guide, you’ll learn how to filter documents by multiple fields in Mongoose effectively, moving from basic to more advanced queries for fine-tuned search functionality.

Basic Query Filtering

const mongoose = require('mongoose');
const { Schema } = mongoose;

const userSchema = new Schema({
  name: String,
  age: Number,
  status: String
});

const User = mongoose.model('User', userSchema);

async function findActiveAdults() {
  const adults = await User.find({ age: { $gte: 18 }, status: 'active' });
  return adults;
}

In the code sample above, we’re filtering documents that represent adult users with an ‘active’ status. The $gte operator is used to match all users who are 18 or older. This is a straightforward query that uses MongoDB’s comparison query operators.

Complex Queries with Logical Operators

async function complexQuery() {
  const users = await User.find({
    $and: [
      { age: { $gte: 18 } },
      { $or: [{ status: 'active' }, { status: 'pending' }] }
    ]
  });
  return users;
}

Logical operators such as $and, $or, and $not enable the combination of multiple query conditions. As shown, we are looking for users who are at least 18 years old and have a status of either ‘active’ or ‘pending’.

Using Query Helpers for Readability

userSchema.query.byStatus = function(status) {
  return this.find({ status: new RegExp(status, 'i') });
};

async function getByStatus(status) {
  const users = await User.find().byStatus(status);
  return users;
}

Custom query helpers can be added to a schema to encapsulate complex logic. Here, a method byStatus is added to filter users by their status using a case-insensitive regular expression.

Filtering with Population

const orderSchema = new Schema({
 user: { type: Schema.Types.ObjectId, ref: 'User' },
 completed: Boolean
});

const Order = mongoose.model('Order', orderSchema);

async function findCompletedOrdersByActiveUsers() {
  const completedOrders = await Order.find({ completed: true }).populate({
    path: 'user',
    match: { status: 'active' }
  });
  return completedOrders.filter(order => order.user);
}

We’re querying for completed orders and simultaneously filtering out orders made by active users by leveraging Mongoose’s populate method. The filter method at the end is required to remove any orders which did not have an active user after population.

Advanced Aggregation Techniques

async function aggregateUserInfo() {
  const result = await User.aggregate([
    { $match: { age: { $gte: 18 } } },
    { $group: { _id: '$status', count: { $sum: 1 } } }
  ]);
  return result;
}

The aggregation framework provides a robust pipeline for transforming and combining documents in complex ways, such as grouping by status and counting user documents within each status. Advanced filters can also be part of the match stages within an aggregation pipeline.

Conclusion

The ability to filter documents by multiple fields in Mongoose is essential for building sophisticated data-driven applications. In this tutorial, we’ve explored a variety of methods to achieve this, such as using query conditionals, logical operators, custom query helpers, population, and aggregation. Embracing these techniques allows you to write efficient, maintainable, and powerful queries that fulfill virtually any data retrieval requirement one might encounter when working with MongoDB through Mongoose.

Next Article: How to create multifield indices in Mongoose

Previous Article: Mongoose: How to sort results by multiple fields

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