Mongoose model.save( ) is not working as expected - node.js

I have this code and MongoDB is passing back an error after I attempt to save a new document:
var data = {
_id: '55d65cfde9cf73322361860b' // _id is a string here
};
var model = new Model(data);
model.save(function (err, result) {
if (err) {
done(err); //the error is captured here in my code
}
else{
done(null, result);
}
});
and I get an error:
MongoError: E11000 duplicate key error index: dev_local_db_smartconnect.jobs.$_id_ dup key: { : ObjectId('55d65cfde9cf73322361860b') }
however, I was under the impression that save would update the model/document if it exists (by using an implicit upsert=true option), and it would use the _id field to look for an existing document.
Does anyone know why this error would occur?
On the other hand, this works for me:
where data is the new data for the model, and _id is a string not an ObjectID, and Model is the mongoose class, not the instance.
Model.update({_id:_id}, {$set: data}, {upsert:true} , function(err,result){
if (err) {
done(err);
}
else if (result) {
done(null, result);
}
else {
done(new Error('grave error'));
}
});

Since you are creating a new local document Mongoose doesn't know it already exists on the server so it will try to save it as a new document. Mongo will then reject the document since an existing document with that ID already exists.
If you query for that document first and then save the returned document it would work as expected. Something like this:
Model.find({id: '55d65cfde9cf73322361860b'}, function (err, doc) {
// update doc with something
// ...
doc.save(function (err, result) {
if (err) {
done(err); //the error is captured here in my code
}
else {
done(null, result);
}
});
});

Related

How to check if a document exisits in MongoDB

I am trying to check if a document exists in MongoDB and then based on that either update the document or create a new one. However only the if statement is being recorded. I have checked that the value of documentExists is null if no document is in mongo.
router.post("/mongo", async function (req, res) {
const documentExists = await Files.findOne({
_id: req.body.id,
});
if (documentExists) {
try {
Files.updateOne(
{ _id: documentExist._id },
{ flag: req.body.flag },
function (err, result) {
if (err) {
res.send(err);
} else {
res.send(result);
}
}
);
} catch (err) {
res.status(400).send(err);
}
} else {
CREATE NEW DOCUMENT
}
})
In your case you can use findOneAndUpdate to update if there is any existing document in the collection, if there is no matching document the query returns null.

How to update the document if it exist else save it?

I am trying to update the document if it exist else save it. I am using below code from this SO question
user.update({User_id:req.body.User_id}, {$setOnInsert: req.body},
{upsert: true}, function (err, data) {
if(err) {
res.json(err);
} else {
res.json('Successfully inserted ');
}
});
but it is throwing the below error
TypeError: oldCb is not a function

Mongoose and Express not reflecting changes to Schema until another change is made?

On a post request to express I make the following update to an array in my User schema:
User.findOne({username: username}, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.status(401).send('No user with that username');
}
if (typeof items === 'number') {
user.update({$push: {cart: items}}, {}, function (err) {
if (err) {
console.log('There was an error adding the item to the cart');
throw err
} else {
console.log('update', user);
res.send(user);
}
});
}
}
When I log the user in express, or in my application, what happens is that the change I make (in this case the addition to the cart) does not show until the next change is made. It is as if the user when logged and sent, is not updated. I know that when checking my database the change is made (item added) but the user being sent in the response is still the original user (from the original response) (i.e. before the change). How to I send the updated user, that I would think would be returned from user.update?
To do what you're trying to do, would involve using the save() method rather than update(), which involves a bit different implementation. This is because calling update() on the model does not modify the instance of the model, it just executes an update statement on the model's collection. Instead, you should use the findOneAndUpdate method:
if (typeof items === 'number') {
User.findOneAndUpdate({username: username}, {$push: {cart: items}}, function(err, user){
// this callback contains the the updated user object
if (err) {
console.log('There was an error adding the item to the cart');
throw err
}
if (!user) {
res.status(401).send('No user with that username');
} else {
console.log('update', user);
res.send(user);
}
})
}
Behind the scenes it does the exact same thing you're doing, executing find() and then update(), except that it also returns the updated object.

Mongoose change document _id on save/update

a quick one:
Why is Mongoose change/upgrading the _id field of a document when I push an update?
Is this an intended behavior?
Thanks.
This is the update I use inside my PUT route, and it returns successfully the updated model, but unfortunately with a new _id for doc
Document.findById(req.params.doc_id, function (err, doc) {
if (err)
res.send(err)
// Do some subdoc stuff here …
doc.save(function (err) {
if (!err) {
console.log('Success!');
res.json(doc);
} else {
console.log(err);
}
});
});
Okay, problem solved:
I was logging the wrong _id (doh!)
Mongoose docs http://mongoosejs.com/docs/2.7.x/docs/model-definition.html
suggest using update or findOne
ex:
var query = { name: 'borne' };
Document.update({"_id": req.params.doc_id}, { name: 'jason borne' }, {}, function(err, numAffected){
if (!err) {
console.log('Success!');
res.json(numAffected);
} else {
console.log(err);
}
});
or
Model.findOne({ "_id": req.params.doc_id }, function (err, doc){
doc.name = 'jason borne';
doc.save();
// here you could use your save instead, but try not to use the doc again
// it is confusing
// doc.save(function (err, documentSaved, numberAffected) {
// if (!err) {
// console.log('Success!');
// res.json(documentSaved);
// } else {
// console.log(err);
// }
// });
});
Later I also found the findById update suggested in some docs http://mongoosejs.com/docs/documents.html, which seems to be up to date, check the version you are using and also double check the two times you are using doc in your functions here. Also you can check your mongoDB and see if there are more than one record getting saved.
db.documents.find( {} )

checking of field in a document

Let's say I have the following schema
var userSchema = new Schema({
name : String
});
var User = mongoose.model('User',userSchema);
EDIT: If an user trying to update field, that does not exists, I need throw exception. My question is how can I check that an updating field does not exists in the updating document. Here is a little example what I need:
app.post('/user/update/:id', function (req, res) {
var field = req.param('field'),
value = req.param('value'),
id = req.param('id');
User.findOne({_id: id},function(err, user){
if(err) throw err;
if (user) {
user[field] = value; // Here is I need to check that field is exists
// in user schema. If does't I have to throw
// an execption.
user.save(function (err){
return res.send(200);
});
}
})
});
Try adding $exists to the query parameter of update(). This will allow you to only update documents if a certain field exists (or not).
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24exists
From the Mongoose v3.1.2 guide:
The strict option, (enabled by default), ensures that values added to our model instance that were not specified in our schema do not get saved to the db. NOTE: do not set to false unless you have good reason.
The strict option may also be set to "throw" which will cause errors to be produced instead of ignoring the bad data.
http://mongoosejs.com/docs/guide.html#strict
var CollectionSchema = new Schema({name: 'string'}, {strict: 'throw'});
Collection.findById(id)
.exec(function (err, doc) {
if (err) {// handle error};
// Try to update not existing field
doc['im not exists'] = 'some';
doc.save(function (err) {
if (err) {
// There is no an errors
}
return res.json(200, 'OK');
});
});
In the expample above I don't get an error when I do update a not existing field.
You can check if the field exists in the schema by using .schema.path(). In your specific use case you can do the following:
app.post('/user/update/:id', function (req, res) {
var field = req.param('field'),
value = req.param('value'),
id = req.param('id');
User.findOne({_id: id},function(err, user){
if(err) throw err;
if (user) {
if(User.schema.path(field)) {
user[field] = value;
} else {
throw new Error('Field [' + field + '] does not exists.');
}
user.save(function (err){
return res.send(200);
});
}
});
});

Resources