Node.js + mongoose [RangeError: Maximum call stack size exceeded] - node.js

I am new to Node.js and I'm facing an error :
RangeError: Maximum call stack size exceeded
I'm not able able to solve the problem because most of the stack problems in others stackoverflow questions about Node.js deals with hundreds of callback but I have only 3 here.
First a fetch (findById) then an update an later a save operation!
My code is :
app.post('/poker/tables/:id/join', function(req, res) {
var id = req.params.id;
models.Table.findById(id, function(err, table) {
if (err) {
console.log(err);
res.send({
message: 'error'
});
return;
}
if (table.players.length >= table.maxPlayers) {
res.send({
message: "error: Can't join ! the Table is full"
});
return;
}
console.log('Table isnt Full');
var BuyIn = table.minBuyIn;
if (req.user.money < table.maxPlayers) {
res.send({
message: "error: Can't join ! Tou have not enough money"
});
return;
}
console.log('User has enought money');
models.User.update({
_id: req.user._id
}, {
$inc: {
money: -BuyIn
}
}, function(err, numAffected) {
if (err) {
console.log(err);
res.send({
message: 'error: Cant update your account'
});
return;
}
console.log('User money updated');
table.players.push({
userId: req.user._id,
username: req.user.username,
chips: BuyIn,
cards: {}
});
table.save(function(err) {
if (err) {
console.log(err);
res.send({
message: 'error'
});
return;
}
console.log('Table Successfully saved with new player!');
res.send({
message: 'success',
table: table
});
});
});
});
});
The error occurs during the save operation at the end!
I use MongoDb with mongoose so Table and User are my database collections.
This is from my first project with Node.js,Express.js and MongoDB so I probably have made huge mistakes in the async code :(
EDIT: I tried to replace the save with an update:
models.Table.update({
_id: table._id
}, {
'$push': {
players: {
userId: req.user._id,
username: req.user.username,
chips: BuyIn,
cards: {}
}
}
}, function(err, numAffected) {
if (err) {
console.log(err);
res.send({
message: 'error'
});
return;
}
console.log('Table Successfully saved with new player!');
res.send({
message: 'success',
table: table
});
});
But it doesn't help the error is still coming and I don't know how to debug it :/

I've been passing for this problem too.
Basically, when you have a property with a ref, and you want to use it in a find, for example, you can't pass the whole document.
For example:
Model.find().where( "property", OtherModelInstance );
this will trigger that error.
However, you have 2 ways to fix this for now:
Model.find().where( "property", OtherModelInstance._id );
// or
Model.find().where( "property", OtherModelInstance.toObject() );
This may stop your problems for now.
There is a issue in their GitHub repo where I reported this, however it's without fix for now. See the issue here.

I kept getting this error and finally figured it out. It's very hard to debug since no real information is presented in the error.
Turns out I was trying to save an object into a field. Saving only a specific property of the object, or JSON stringifying it, worked like a charm.
Seems like it would be nice if the driver gave a more specific error, but oh well.

MyModel.collection.insert causes:
[RangeError: Maximum call stack size exceeded]
When you pass array of instances of MyModel instead of just array with values of that objects.
RangeError:
let myArray = [];
myArray.push( new MyModel({ prop1: true, prop2: false }) );
MyModel.collection.insert(myArray, callback);
No error:
let myArray = [];
myArray.push( { prop1: true, prop2: false } );
MyModel.collection.insert(myArray, callback);

There are a few ways to debug nodejs applications
Built-in Debugger
The Node.js debugger is documented here: http://nodejs.org/api/debugger.html
To place breakpoints, simply put debugger; at the place you want to break. As you said the table.save callback is giving you troubles, you could put a breakpoint inside that function.
Then you run node with the debugger enabled:
node debug myscript.js
And you will get more helpful output.
Investigating the stack
You can also use console.trace to print a stacktrace, if you have a good idea of when/where you run into problems, and want to figure out how you got there.
Good luck, I hope this helps!

Related

Posting multiple documents using postman not working

I'm trying to post data using postman, but when I tried to use multiple documents, it shows blank in my mongodb. But when I try to insert a single document, it saves the data. Please correct me, I'm new to this
server.js
app.post('/bloodinventory', function(req, res) {
var bloodinventory= new Bloodinventories();
bloodinventory.blood_group = req.body.blood_group;
bloodinventory.blood_category = req.body.blood_category;
bloodinventory.num_stock = req.body.num_stock;
bloodinventory.save(function(err) {
if (err) {
res.json({ success: false, message: 'Blood Donation already exists!' });
} else {
res.json({ success: true, message: 'Blood Donation Created!' });
}
});
});
Then in my postman, I tried to insert this data:
{
"bloodinventories":[
{
"blood_group":"A_positive",
"blood_category":"whole blood",
"num_stock":11
},
{
"blood_group":"A_negative",
"blood_category":"platelet",
"num_stock":9
}
]
}
Then nothing shows in my mongodb, result:
_id:5c45c6a495788ec2c47f8c8b
__v:0
When you provide single document in your POST request
Example:-
{
"blood_group":"A_positive",
"blood_category":"whole blood",
"num_stock":11
}
it easly get request.body.blood_group, request.body.blood_category and show on
But when you pass Mutliple document in POST request in array form
{
"bloodinventories":[
{
"blood_group":"A_positive",
"blood_category":"whole blood",
"num_stock":11
},
{
"blood_group":"A_negative",
"blood_category":"platelet",
"num_stock":9
}
]
}
Now your request is getting a array so your request body contain bloodinventories. And you can access it as request.body.bloodinventories
Take this const and apply loop on it and insert all document.
Or Simply make API like this and always send array in every request of insert.
Bloodinventories.insertMany(request.body.bloodinventories).then((result) => {
//Success Message
}).catch(err => {
// Error Message
});
It May Help For You...

Update data in MongoDB with Mongojs using findAndModify()

Yet another first-timer problem here. This gets data from a database and displays it in some text fields (that part is not shown in the code below) and after the user edits it the data should be updated in the database via the findAndModify() method and I think this is where the issue lies. There are no errors, it just doesn't do anything. EDIT The following error is received: MongoError: Either an update or remove=true must be specified
server.js
MongoClient.connect("mongodb://user:secretPassword#aws-us-east-1-portal.7.dblayer.com:10712,aws-us-east-1-portal.10.dblayer.com:10316/database", function(err, db) {
if (err) throw err;
var contactList = db.collection("contactList");
app.put('/contactList/:id', function(req, res) {
var id = req.params.id;
console.log("edited: " + req.body.name); //works up until here
contactList.findAndModify({
query: {_id: mongojs.ObjectId(id)},
update: {$set: {name: req.body.name, email: req.body.email, number: req.body.number}},
new: true
}, function (err, doc) {
res.json(doc);
})
});
controller.js
$scope.update = function() {
$http.put('/contactList/' + $scope.contact._id, $scope.contact).success(function(response) {
refresh();
})
};
If this were me I would first do a couple of things:
Before your call to findAndModify just do a simple find using your query. Make sure you can actually find the object using your query. If that works you know that the 'find' part of the findAndModify is probably ok.
Do some console logging inside the callback handler of the findAndModify call. As it stands you do not do anything if an err is returned from the findAndModify call. It is possible your call is returning an error that you are just ignoring and it may provide some additional insight into your problem.
I would try these two first and see if it helps.
Update:
Example using native:
collection.findAndModify(
{ field: 'some value' },
[],
{ $set: { field2: 'some new value' } },
{ new:true },
function(err, doc) {
//handle err and doc
});

Mongoose Returned Model can't be updated

I'm pretty new to Mongoose/Mongo and node.js, so I suspect this is just a misunderstanding on my side, but...
The code sample below is the smallest failing example, not specifically my use case.
var User = app.db.model('User');
User.find({email: 'm8#test.com'}, function (err, models) {
models[0].update(function(err, mod) {
console.log(err.message)
});
});
This results in the following error: After applying the update to the document {_id: ObjectId('54647402cb955748153ea782') , ...}, the (immutable) field '_id' was found to have been altered to _id: ObjectId('546d9e0e539ed9ec102348f9')
Why is this happening? I would have thought calling update on the model returned from the initial find would have been fine.
Please note: in my use case there are things happening in between the find and the update. Specifically, I'm doing something similar to:
model.property.push(objectId)
Which I then want to commit via the update.
I'm sure this is a straight-forward issue, but I can't see anywhere in the docs I may be getting it wrong.
All help appreciated.
UPDATE:
What I actually needed to do was:
var User = app.db.model('User');
User.find({email: 'm8#test.com'}, function (err, models) {
models[0].save(function(err, mod) {
console.log(err.message)
});
});
Using 'save' rather than 'update'
I don't know if I understood
Find and Update ( for example using express )
var email = req.params.email;
User.find({email:email}, req.body, function(err,user){
if(err){
throw err;
}
//you do stuff like this
var obj = {
password:'new pass',
username:'username'
}
//use save if you want validate
User.update(user[0],obj,function(err, mod) {
console.log(err)
});
});
Only Update: ( for example using express )
User.update({email:email}, req.body, {}, function(err,user){
if(err){
throw err;
}
res.send(200, {
message : 'User updated ' + user
});
});
Remember that:
A model is a compiled version of the schema.
I hope this can help you

Sequelize.js transactions not rolling back

Hi got a question of sequelize js and transactions.
So what i am trying to do is nest inserts and commit on success, rollback otherwise here is a snippet which isn't working for me for some reason or another.
sequelize()
.transaction(function(t){
myModel
.create({
name: 'shawn;
}, {transaction: t})
.success(function(newModel) {
myModel
.create({
name: 'shawn;
}, {transaction: t})
.success(function(newModel) { t.commit()})
.error(function(err) {t.rollback()})
}
.error(function(err) {t.rollback()});
});
Don't mind any syntax error the logic which i am looking to debug. The problem occurs when i replace the commit statement on success with a rollback i dont get the 2 inserts rows rolled back?
Regards
Shawn
debug this, if error exist, you see message in console and rollback is run, else ...
sequelize()
.transaction(function(t){
myModel.create({name: 'shawn'}).success(function(newModel) {
console.log('success1')
myModel.create({name: 'shawn'})
.success(function(newModel){
console.log('success2');
t.commit();
})
.error(function(err){
console.log('error2')
t.rollback();
});
}).error(function(err) {
console.log('error1')
t.rollback()
});
});

Mongoose unique validation error type

I'm using this schema with mongoose 3.0.3 from npm:
var schema = new Schema({
_id: Schema.ObjectId,
email: {type: String, required: true, unique: true}
});
If I try to save a email that is already in db, I expect to get a ValidationError like if a required field is omitted. However this is not the case, I get a MongoError: E11000 duplicate key error index.
Which is not a validation error (happens even if I remove the unique:true).
Any idea why?
I prefer putting it in path validation mechanisms, like
UserSchema.path('email').validate(function(value, done) {
this.model('User').count({ email: value }, function(err, count) {
if (err) {
return done(err);
}
// If `count` is greater than zero, "invalidate"
done(!count);
});
}, 'Email already exists');
Then it'll just get wrapped into ValidationError and will return as first argument when you call validate or save .
I had some issues with the approved answer. Namely:
this.model('User') didn't work for me.
the callback done wasn't working properly.
I resolved those issues by:
UserSchema.path('email').validate(async (value) => {
const emailCount = await mongoose.models.User.countDocuments({email: value });
return !emailCount;
}, 'Email already exists');
I use async/await which is a personal preference because it is much neater: https://javascript.info/async-await.
Let me know if I got something wrong.
This is expected behavior
The unique: true is equivalent to setting an index in mongodb like this:
db.myCollection.ensureIndex( { "email": 1 }, { unique: true } )
To do this type of validation using Mongoose (Mongoose calls this complex validation- ie- you are not just asserting the value is a number for example), you will need to wire in to the pre-save event:
mySchema.pre("save",function(next, done) {
var self = this;
mongoose.models["User"].findOne({email : self.email},function(err, results) {
if(err) {
done(err);
} else if(results) { //there was a result found, so the email address exists
self.invalidate("email","email must be unique");
done(new Error("email must be unique"));
} else {
done();
}
});
next();
});
Simply response to json
try {
let end_study_year = new EndStudyYear(req.body);
await end_study_year.save();
res.json({
status: true,
message: 'បានរក្សាទុក!'
})
}catch (e) {
res.json({
status: false,
message: e.message.toString().includes('duplicate') ? 'ទិន្នន័យមានរួចហើយ' : e.message.split(':')[0] // check if duplicate message exist
})
}
Sorry for answering an old question. After testing I feel good to have find these answers, so I will give my experience. Both top answers are great and right, just remember that:
if your document is new, you can just validate if count is higher than 0, thats the common situation;
if your document is NOT new and has modified the unique field, you need to validate with 0 too;
if your document is NOT new and has NOT being modified, just go ahead;
Here is what I made in my code:
UserSchema.path('email').validate(async function validateDuplicatedEmail(value) {
if (!this.isNew && !this.isModified('email')) return true;
try {
const User = mongoose.model("User");
const count = await User.countDocuments({ email: value });
if (count > 0) return false;
return true;
}
catch (error) {
return false;
}
}, "Email already exists");

Resources