skip to Main Content

I got some error that says ‘Cannot do inclusion on field guides in exclusion projection’, this happen bcs I tried to populate data using pre middleware in my schema:

const tourSchema = new mongoose.Schema({
 ...
 guides: [
    {
      type: mongoose.Schema.ObjectId,
      ref: 'User'
    }
  ]
});

tourSchema.pre(/^find/, function(next) {
  this.populate({ path: 'guides' });
  next();
});

but the error happen only when accessing getAllTour handlers when access only specified tour the error does not showed up

// Errors happens here
exports.getAllTours = catchAsync(async (req, res, next) => {
  const features = new APIFeatures(Tour.find(), req.query)
    .filter()
    .sort()
    .limitFields()
    .paginate();
  const tours = await features.query;
  res.status(200).json({
    status: 'success',
    length: tours.length,
    data: {
      tours
    }
  });
});
// But in here there is no error
exports.getTour = catchAsync(async (req, res, next) => {
  const tour = await Tour.findById(req.params.id);
  if (!tour) {
    return next(
      new AppError('No tour found! please check the ID correctly', 404)
    );
  }
  res.status(200).json({
    status: 'success',
    requestedAt: req.requestTime,
    data: {
      tour
    }
  });
});

you can also check my code in this link

Solutions for my errors

2

Answers


  1. Chosen as BEST ANSWER

    After debugging my code from previous answer, the errors is gone by made little bit changes from limitFields():

    else if (this.queryString._fieldsForExec) {
          const fields = this.query
            ._fieldsForExec()
            .filter(f => !['createdAt', '_id', '__v'].includes(f));
          this.query = this.query.select(fields);
        }
        return this;
    

    by adding this.queryString._fieldsForExec as a parameter, this can get rid of the undefined from .filter.


  2. In flux-tours/utils/apiFeatures.js, you have this method where by default you’re excluding some fields:

    limitFields() {
        if (this.queryString.fields) {
          const fields = this.queryString.fields.split(',').join(' ');
          this.query = this.query.select(fields);
        } else {
          this.query = this.query.select('-createdAt _id -__v');
        }
        return this;
    }
    

    While in your flux-tours/models/tourModel.js you are including the guides field using middleware.
    According to Mongoose docs, you cannot do both:

    A projection must be either inclusive or exclusive. 
    In other words, you must either list the fields to include (which excludes all others), 
    or list the fields to exclude (which implies all other fields are included). 
    The _id field is the only exception because MongoDB includes it by default.
    

    What you can do instead, is providing the default list of fields to select explicitly in the else branch of limitFields().

    Or, you can try getting the full list of field names using query._fieldsForExec() and then remove in code the createdAt _id __v fields:

    ... else {
      const fields = query._fieldsForExec()
                          .filter(f => !['createdAt', '_id', '__v'].includes(f));
      this.query = this.query.select(fields);
    }
    ...
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search