1

I have 2 schemas

const schema = Schema({
    headLine: {
        type: String,
        required: false
    },
    availableDays: [{
        type: Schema.Types.ObjectId,
        ref: AvailableDay
    }]
}, {collection: 'providers', timestamps: true});

module.exports = mongoose.model("Provider", schema);

const schema = Schema({
    day: {
        type: String,
        enum: ['Mondays','Tuesdays','Wednesdays','Thursdays','Fridays','Saturdays','Sundays']
    },
    timeFrom: String,
    timeTo: String
}, {collection: 'availableDays', timestamps: true});

module.exports = mongoose.model("AvailableDay", schema);

Then in a route I call to a repository like this

router.get('/', async (req, res) => {


    const match = {};
    const sort  = {};
    const options  = {};


    // Arrange sort
    if(req.query.sortBy){
        const sortArray = JSON.parse(req.query.sortBy);
        sortArray.map(e => sort[e[0]] = e[1] && e[1] === 'desc' ? -1 : 1);
        options['sort'] = sort
    }

    // Get the pagination: limit how many, skip where it starts
    if(req.query.limit) {
        options['limit'] = parseInt(req.query.limit);
    }
    if(req.query.skip) {
        options['skip'] = parseInt(req.query.skip);
    }

    const docs = await ProviderRepository.findBy(match, {}, options);

    res.status(200).json(docs)

});

So what I need here is to filter providers for an AvailableDay monday and return the docs and count the total docs for pagination. I'm doing something like this without success

const findBy = async (params, projection = "", options = {}, callback) => {
    const data = () => {
        Provider.find(params, projection, options)
            .populate([{path: 'user', match: {gender: 'F'}}]).exec((error, e) => {
            if (error) {
                console.log('error:', error)
                return {error: error}; // returns error in json
            }
            return e.filter(i => i.user);
        });
    };


        const total = await Provider.countDocuments(params).exec();
    return {data(), total}

}

Thanks in advance

Braian Mellor
  • 1,934
  • 3
  • 31
  • 50

1 Answers1

0

Use mongoose-aggregate-paginate-v2 and update your schema. If you use that package then you have to convert your queries from populate to aggregate style.

STEP 1: Update schema. Sample Schema:

const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-aggregate-paginate-v2');
const Schema = mongoose.Schema;

let definition = {
  headLine: {
    type: String,
    required: false
  },
  availableDays: [{
    type: Schema.Types.ObjectId,
    ref: AvailableDay
  }]
};

let options = {
  collection: 'providers'
};

let providerSchema = new Schema(definition, options);
providerSchema.plugin(mongoosePaginate);

module.exports = mongoose.model('providers', providerSchema);

STEP 2: Update controller. Sample code in controller:

router.get('/', async (req, res) => {
  const match = {}

  const sort = {
    // Fill it based on your sort logic.
  }

  const paginateOptions = {
    page: req.query.page,       // Page number like: 1, 2, 3...
    limit: req.query.limit      // Limit like: 10, 15, 20...
  };

  ProviderRepository
    .findBy(match, {}, sort, paginateOptions)
    .then(() => {
      res.status(200).json(docs)
    })
    .catch(() => {
      res.status(HTTP_ERROR_CODE).json({ "error": "Your error message" })
    })
});

STEP 3: Update manager. Sample code in manager:

const findBy = (match, projection, sort, paginateOptions) => {
  if (!paginateOptions) {
    paginateOptions = {
      pagination: false
    };
  }

  let providerAggregate = providerSchema.aggregate([
    {
      $lookup: {
        from: "availableDays",
        let: { days: "$availableDays" },
        pipeline: [
          {
            $match: {
              $expr: { 
                $in: ["$$availableDays", "$day"]
              }
            }
          }
        ],
        as: "availableDays"
      }
    },
    {
      $lookup: {
        from: "users",                  // I dont know the collection name
        let: { user_id: "$user" }
        pipeline: [
          {
            $match: {
              "gender": 'F',
              $expr: { 
                $eq: ["$_id", "$$user_id"]
              }
            }
          }
        ],
        as: "users"
      }
    }
    { $sort: sort }
  ]);

  return providerSchema
    .aggregatePaginate(providerAggregate, paginateOptions)
    .then(res => {
      return res;
    })
    .catch(err => {
      throw err;
    });
};
Dheemanth Bhat
  • 4,269
  • 2
  • 21
  • 40
  • hi, thanks for the answer, pretty complete, but what should I send in conditions and in project? – Braian Mellor Feb 11 '21 at 08:35
  • Also how can I populate there? – Braian Mellor Feb 11 '21 at 08:43
  • What need to be put in conditions and project depends on your requirement. Its optional you can skip them if not needed. populate is alternative for aggregate offered by mongoose library and its not native to MongoDB. Do another $lookup operation to populate user data. I will update the answer. Please check this out https://stackoverflow.com/questions/55575806/mongoose-populate-vs-aggregate – Dheemanth Bhat Feb 11 '21 at 15:43