Sling Academy
Home/Node.js/Grouping Results in Mongoose (with examples)

Grouping Results in Mongoose (with examples)

Last updated: December 30, 2023

Grouping results in MongoDB can be a powerful tool for data analysis and manipulation. Mongoose, a popular ODM library for MongoDB and Node.js, supports aggregation and grouping through its intuitive API. This tutorial will walk you through various examples showcasing how to use grouping in Mongoose effectively.

Getting Started

Before grouping results, ensure you have Mongoose and a MongoDB instance set up. Use the following code snippet to initiate a connection to your database:

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/myDatabase', { useNewUrlParser: true, useUnifiedTopology: true });

const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  // we're connected!
  console.log('Connected to the database');
});

Basic Grouping with Aggregation

Let’s consider a simple example where we have a collection ‘User’, which stores user details.

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

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

To group users by country and count them, follow this aggregate pipeline:

const groupByCountry = async () => {
  try {
    const result = await User.aggregate([
      { $group: { _id: '$country', count: { $sum: 1 } } }
    ]);
    console.log(result);
  } catch (error) {
    console.error(error);
  }
};

groupByCountry();

Advanced Groupings

For more complex grouping, you can add additional stages to the pipeline, including match, sort, and project stages:

const advancedGrouping = async () => {
  try {
    const result = await User.aggregate([
      { $match: { age: { $gt: 18 } } },
      { $group: { _id: '$country', avgAge: { $avg: '$age' } } },
      { $sort: { avgAge: -1 } }
    ]);
    console.log(result);
  } catch (error) {
    console.error(error);
  }
};

advancedGrouping();

Grouping and Populating References

Often, you would need to populate documents referenced by ObjectId in the grouping process. Mongoose enables this as shown below:

const groupAndPopulate = async () => {
  try {
    const result = await User.aggregate([
      // Assuming users have posts referenced by ObjectId
      { $lookup: { from: 'posts', localField: '_id', foreignField: 'user', as: 'posts' } },
      { $unwind: '$posts' },
      // Now group by user id and count their posts
      { $group: { _id: '$_id', postCount: { $sum: 1 } } },
      { $sort: { postCount: -1 } }
    ]);
    console.log(result);
  } catch (error) {
    console.error(error);
  }
};

groupAndPopulate();

When performing group operations, it’s important to consider the potential impact on performance. Ensure appropriate indexes are in place to optimize the aggregation queries.

Using Grouping with MapReduce

Mongoose also allows grouping through the MapReduce paradigm:

const mapReduceExample = async () => {
  const mapFunction = function() { emit(this.country, 1); };
  const reduceFunction = function(keyCountry, countArray) { return Array.sum(countArray); };

  try {
    const result = await User.mapReduce({
      map: mapFunction,
      reduce: reduceFunction
    });
    console.log(result);
  } catch (error) {
    console.error(error);
  }
};

mapReduceExample();

Conclusion

In this guide, we explored the essentials of grouping results in Mongoose with practical examples. Remember, the more complex your queries, the more thought you should put into the performance implications. Happy coding!

Next Article: How to Store JSON data with Mongoose

Previous Article: Mongoose: How to find and update subdocuments

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