Anirudh
Anirudh

Reputation: 3358

How to filter results in mongoose based on multiple conditions in populate?

I have a list of users to be displayed. I have 2 filters

1) Areas

2) Categories

I get proper filtered results when I apply only 1 filter i.e filter users based on selected categories or filter users based on areas.

Now I need to filter users based on both Categories and Areas selection. How should I approach to get the desired result?

Below is my working code to filter users based on selected categories:

    User.find().populate('city').populate( {path: 'categories', match: { name: { $in: filterCatArray }}} ).populate('area').exec(function (err, users) {

         users = users.filter(function(user) {
                return user.categories; // return only filtered users 
           });

         data = {fulldata:users}; 
         res.render('home',{data:data});

    });

User schema:

      var userSchema = new Schema({
      city: {type: mongoose.Schema.Types.ObjectId, ref: 'City',required: true},
      area: {type: mongoose.Schema.Types.ObjectId, ref: 'Area', required: true},
      categories: [{type: mongoose.Schema.Types.ObjectId, ref: 'Category', required: true}],
      status: { type: Boolean, default: true },
      created_at: Date,
      updated_at: Date
    });

Area Schema

    var areaSchema = new Schema({
      city: {type: mongoose.Schema.Types.ObjectId, ref: 'City'},
      name: { type: String, required: true},
      status: { type: Boolean, default: true },
      created_at: Date,
      updated_at: Date
    });

Category Schema

    var catSchema = new Schema({
      name: { type: String, required: true},
      status: { type: Boolean, default: true },
      created_at: Date,
      updated_at: Date
    });

City Schema

    var citySchema = new Schema({
      name: { type: String, required: true},
      status: { type: Boolean, default: true },
      created_at: Date,
      updated_at: Date
    });

Upvotes: 1

Views: 2283

Answers (1)

Mikey
Mikey

Reputation: 6766

I'm new to aggregation, but I recently started using it. I think populate is great for simple cases, but when you want to start doing complex stuff such as filtering results based off other collections, you need to use aggregation.

This is untested but it would look roughly like this:

UserSchema.aggregate([
    // 1 join city collection
    {
        $lookup: {
            from: 'citys',
            localField: 'city',
            foreignField: '_id',
            as: 'city'
        }
    },
    {
        $unwind: '$city'
    },
    // 2 join area collection
    {
        $lookup: {
            from: 'areas',
            localField: 'area',
            foreignField: '_id',
            as: 'area'
        }
    },
    {
        $unwind: '$area'
    },
    // 3 join categories collection
    {
        $unwind: '$categories'
    },
    {
        $lookup: {
            from: 'categories',
            localField: 'categories',
            foreignField: '_id',
            as: 'category'
        }
    },
    // 4 filter results that only have specified categories
    {
        $match: { 
            'category.name': { $in: filterCatArray },
            'area.name': 'something' // just an example
        }
    },
    // 5 group results by user
    {
        $group: {
            _id: '$_id',
            city: { $first: '$city' },
            area: { $first: '$area' },
            categories: { $push: '$category' },
            status: { $first: '$status' },
            created_at: { $first: '$created_at' },
            updated_at: { $first: '$updated_at' },
        }
    }
], function (err, users) {

});

If there is a problem, comment each stage of the pipeline starting from the bottom to see what is happening.

For more information, read the following links:

Upvotes: 2

Related Questions