简体   繁体   中英

RangeError: Maximum call stack size exceeded on Nodejs + Mongoose built API

Im receiving a maximum call stack size exceeded error while working with mongoose and Nodejs. here is the error

RangeError: Maximum call stack size exceeded
    at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2045:24)
    at model.Document.toJSON (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2362:15)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:252:18)
    at cloneArray (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:362:14)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:247:12)
    at cloneObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:343:13)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:260:16)
    at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2092:13)

I believe this means that I'm causing an infinite loop somewhere, but I am unsure what is causing the error. The route worked correctly, then I added the mongoose-autopopulate plugin to the app. When sending a POST correctly with a token to my drive post route, I recieve this error. Nothing is logged and the server stops.

Here is an example of my route in drive.js

   router.post('/', function(req, res, next){
  var decoded = jwt.decode(req.query.token);
  User.findById(decoded.user._id, function(err, user){
    if (err) {
      return res.status(500).json({
        title: 'There was a server error',
        error: err
      });
    }
    var drive = new Drive({
      startAddress: req.body.startAddress,
      endAddress: req.body.endAddress,
      tripDate: req.body.tripDate,
      tripHour: req.body.tripHour,
      price: req.body.price,
      numberOfPassengers: req.body.numberOfPassengers,
      user: user
    });
    drive.save(function (err, result) {
      if(err) {
        return res.status(500).json({
          title: 'There was an error saving the drive collection',
          error: err
        });
      }
      user.drives.push(result);
      user.save();
      res.status(201).json({
        message: 'Drive saved',
        obj: result
      });
    });
  });
});

And here is the related model

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.model');
var Trip = require('./trip.model');
var autoPopulate = require('mongoose-autopopulate');



var driveSchema = new Schema({
  startAddress: {type: String, required: true, lowercase: true},
  endAddress: {type: String, required: true, lowercase: true},
  tripDate: {type: Date, required: true},
  tripHour: {type: String, required: true},
  price: {type: Number, required: true},
  numberOfPassengers: {type: Number, required: true},
  trip: {type: Schema.Types.ObjectId, ref: 'Trip', autopopulate: true},
  user: {type: Schema.Types.ObjectId, ref: 'User', autopopulate: true}
});

driveSchema.post('remove', function(drive) {
  User.findById(drive.user, function(err, user) {
    user.drives.pull(drive);
    user.save();
  });
});

driveSchema.plugin(autoPopulate);


module.exports = mongoose.model('Drive', driveSchema);

All of my models follow the same methods and queries. Is there anything in specific I am doing wrong? I looked it up and it seems that I could be calling an instance instead of JSON which breaks the code, but Im not experienced enough to identify where that instance is, or whats causing a reocurring call as .find() or .where() that I am using that is breaking it.

Here are my other models

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var autopopulate = require('mongoose-autopopulate');

var tripSchema = new Schema({
  tripActivated: {type: Boolean},
  tripCompleted: {type: Boolean},
  driver: {type: Schema.Types.ObjectId, ref: 'Drive', autopopulate: true},
  riders: [{type: Schema.Types.ObjectId, ref: 'Ride', autopopulate: true}],
  comments: [{type: Schema.Types.ObjectId, ref: 'Comment', autopopulate: true}]
});

tripSchema.post('remove', function(trip) {
  User.findById(trip.driver.user, function(err, user) {
    user.trips.pull(trip);
    user.save();
  });
});


// tripSchema.post('remove', function(trip) {
//   User.findById(trip.riders.user, function(err, user) {
//     user.trips.pull(trip);
//     user.save();
//   });
// });

tripSchema.plugin(autopopulate);

module.exports = mongoose.model('Trip', tripSchema);


//// NEW MODEL

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.model');
var Trip = require('./trip.model');
var autoPopulate = require('mongoose-autopopulate');



var rideSchema = new Schema({
  startAddress: {type: String, required: true, lowercase: true},
  endAddress: {type: String, required: true, lowercase: true},
  tripDate: {type: Date, required: true},
  tripHour: {type: String, required: true},
  numberOfPassengers: {type: Number, required: true},
  trip: {type: Schema.Types.ObjectId, ref: 'Trip', autopopulate: true},
  user: {type: Schema.Types.ObjectId, ref: 'User', autopopulate: true}
});

rideSchema.post('remove', function(ride) {
  User.findById(ride.user, function(err, user) {
    user.rides.pull(ride);
    user.save();
  });
});

rideSchema.plugin(autoPopulate);


module.exports = mongoose.model('Ride', rideSchema);


////// NEW MODEL

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.model');
var autoPopulate = require('mongoose-autopopulate');


var requestSchema = new Schema({
  typeOfRequest: {type: String, required: true},
  driver: {type: Schema.Types.ObjectId, ref: 'Drive', autopopulate: true},
  rider: {type: Schema.Types.ObjectId, ref: 'Ride', autopopulate: true}
});

requestSchema.post('remove', function(request) {
  User.findById(request.user, function(err, user) {
    user.requests.pull(request);
    user.save();
  });
});

requestSchema.plugin(autoPopulate);


module.exports = mongoose.model('Request', requestSchema);

//// NEW MODEL

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var autoPopulate = require('mongoose-autopopulate');


var User = require('./user.model');

var messageSchema = new Schema ({
  content: {type: String, required: true},
  receiver: {type: Schema.Types.ObjectId, ref: 'User', autopopulate: true },
  user: {type: Schema.Types.ObjectId, ref: 'User', autopopulate: true}
});

messageSchema.post('remove', function(message) {
  User.findById(message.user, function(err, user) {
    user.messages.pull(message);
    user.save();
  });
});

messageSchema.plugin(autoPopulate);


module.exports = mongoose.model('Message', messageSchema);

Upon diving deeper, my error seems to stem from this code that is starred in the mongoose source code in node_modules

Document.prototype.$toObject = function(options, json) {
  ***var defaultOptions = {
    transform: true,
    json: json,
    retainKeyOrder: this.schema.options.retainKeyOrder,
    flattenDecimals: true
  };***

  // _isNested will only be true if this is not the top level document, we
  // should never depopulate
  if (options && options.depopulate && options._isNested && this.$__.wasPopulated) {
    // populated paths that we set to a document
    return clone(this._id, options);
  }

  // When internally saving this document we always pass options,
  // bypassing the custom schema options.
  if (!(options && utils.getFunctionName(options.constructor) === 'Object') ||
      (options && options._useSchemaOptions)) {
    if (json) {
      options = this.schema.options.toJSON ?
        clone(this.schema.options.toJSON) :
        {};
      options.json = true;
      options._useSchemaOptions = true;
    } else {
      options = this.schema.options.toObject ?
        clone(this.schema.options.toObject) :
        {};
      options.json = false;
      options._useSchemaOptions = true;
    }
  }

  for (var key in defaultOptions) {
    if (options[key] === undefined) {
      options[key] = defaultOptions[key];
    }
  }

  ('minimize' in options) || (options.minimize = this.schema.options.minimize);

  // remember the root transform function
  // to save it from being overwritten by sub-transform functions
  var originalTransform = options.transform;

  options._isNested = true;

  var ret = clone(this._doc, options) || {};

  if (options.getters) {
    applyGetters(this, ret, 'paths', options);
    // applyGetters for paths will add nested empty objects;
    // if minimize is set, we need to remove them.
    if (options.minimize) {
      ret = minimize(ret) || {};
    }
  }

  if (options.virtuals || options.getters && options.virtuals !== false) {
    applyGetters(this, ret, 'virtuals', options);
  }

  if (options.versionKey === false && this.schema.options.versionKey) {
    delete ret[this.schema.options.versionKey];
  }

  var transform = options.transform;

  // In the case where a subdocument has its own transform function, we need to
  // check and see if the parent has a transform (options.transform) and if the
  // child schema has a transform (this.schema.options.toObject) In this case,
  // we need to adjust options.transform to be the child schema's transform and
  // not the parent schema's
  if (transform === true ||
      (this.schema.options.toObject && transform)) {
    var opts = options.json ? this.schema.options.toJSON : this.schema.options.toObject;

    if (opts) {
      transform = (typeof options.transform === 'function' ? options.transform : opts.transform);
    }
  } else {
    options.transform = originalTransform;
  }

  if (typeof transform === 'function') {
    var xformed = transform(this, ret, options);
    if (typeof xformed !== 'undefined') {
      ret = xformed;
    }
  }

  return ret;
};

I set mongoose.set('debugger', true); to get a better error. When attempting to post on ride.js, the app registers the POST request, finds the userID, handles the new Ride (based off model), inserts the ride into the database, and then crashes immediately after.

here is the error with mongoose logs

Mongoose: users.ensureIndex({ email: 1 }, { unique: true, background: true })
Successfully connected to localhost:27017/atlas
Mongoose: users.findOne({ _id: ObjectId("59506e1629cdff044664f21c") }, { fields: {} })
(node:1219) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
Mongoose: rides.insert({ startAddress: 's', endAddress: 's', tripDate: new Date("Fri, 22 Feb 2222 00:00:00 GMT"), tripHour: '8', numberOfPassengers: 2, user: ObjectId("59506e1629cdff044664f21c"), _id: ObjectId("595078cf0b46e704c3091070"), __v: 0 })
events.js:160
      throw er; // Unhandled 'error' event
      ^

RangeError: Maximum call stack size exceeded
    at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2045:24)
    at model.Document.toJSON (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2362:15)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:253:18)
    at cloneArray (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:363:14)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:247:12)
    at cloneObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:344:13)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:261:16)
    at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2092:13)
    at model.Document.toJSON (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2362:15)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:253:18)
    at cloneObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:344:13)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:261:16)
    at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2092:13)
    at model.Document.toJSON (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2362:15)
    at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:253:18)
    at cloneArray (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:363:14)

**********UPDATE

After doing some digging, this is the code thats breaking the app

user.drives.push(result); <----------
          user.save();

Its the inserting the data into mongodb and then when it tries to push to the user, it breaks. Any idea why?I added my user model for reference.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var mongooseUniqueValidator = require('mongoose-unique-validator');
var autoPopulate = require('mongoose-autopopulate');


    var userSchema = new Schema({
      email: {type: String, required: true, unique: true, lowercase:true},
      password: {type: String, required: true},
      profileImgUrl: {type: String},
      fName: {type: String},
      lName: {type: String},
      yearOfBirth: {type: String},
      gender: {type: String},
      ratings: [{type: Number}],
      comments: [{type: Schema.Types.ObjectId, ref: 'Comment', autopopulate: true}],
      messages: [{type: Schema.Types.ObjectId, ref: 'Message', autopopulate: true}],
      rides: [{type: Schema.Types.ObjectId, ref: 'Ride', autopopulate: true}],
      drives: [{type: Schema.Types.ObjectId, ref: 'Drive', autopopulate: true}],
      requests: [{type: Schema.Types.ObjectId, ref: 'Request', autopopulate: true}],
      trips: [{type: Schema.Types.ObjectId, ref: 'Trip', autopopulate: true}]
    });

    userSchema.plugin(mongooseUniqueValidator);
    userSchema.plugin(autoPopulate);

    module.exports = mongoose.model('User', userSchema);

The issue was in my user model. I was using the mongoose-autopopulate plugin and setting autopopulate to true objects that had a user instance in them.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var mongooseUniqueValidator = require('mongoose-unique-validator');
var autoPopulate = require('mongoose-autopopulate');


    var userSchema = new Schema({
      email: {type: String, required: true, unique: true, lowercase:true},
      password: {type: String, required: true},
      profileImgUrl: {type: String},
      fName: {type: String},
      lName: {type: String},
      yearOfBirth: {type: String},
      gender: {type: String},
      ratings: [{type: Number}],
      comments: [{type: Schema.Types.ObjectId, ref: 'Comment', autopopulate: true}],
      messages: [{type: Schema.Types.ObjectId, ref: 'Message', autopopulate: true}],<---------
      rides: [{type: Schema.Types.ObjectId, ref: 'Ride', autopopulate: true}],<---------
      drives: [{type: Schema.Types.ObjectId, ref: 'Drive', autopopulate: true}],<-----------
      requests: [{type: Schema.Types.ObjectId, ref: 'Request', autopopulate: true}],
      trips: [{type: Schema.Types.ObjectId, ref: 'Trip', autopopulate: true}]
    });

    userSchema.plugin(mongooseUniqueValidator);
    userSchema.plugin(autoPopulate);

    module.exports = mongoose.model('User', userSchema);

I was also setting autopopulate to true on those models. This was calling an infinite loop between populating the model given, and the user model.

If anyone is having this issue. Don't be like me and post a bunch of code on stack overflow. Figure out what you're calling that's recalling the call. In my case, I was calling autopopulate on two models that would communicate back and forth.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM