Update fields of Mongoose document object from request body - node.js

The question(Mongoose save all parameters from request body) shows how to create new mongoose document object with request body in a line of code.
Extending this question, I want to know how to set all fields of mongoose document object at once.
router.route('/car')
.put(function(req, res, next) {
Car.findById(req.params.id, function (err, car) {
if (err) {
res.status(400).send(err);
} else {
if (!car)
return next(new Error('Failed to load car: ' + req.params.id));
car.name = req.body.name; // Code refactoring required
car.seats = req.body.seats;
car.engine_size = req.body.engine_size;
car.save(function(err) {
if(err)
res.status(400).send(err);
else
res.json(car);
});
}
});
});
In creating document var car = new Car(req.body); is perfect way to fill fields from request body.
For updating exist mongoose document, is there better way than below lines:
car.name = req.body.name;
car.seats = req.body.seats;
car.engine_size = req.body.engine_size;

You should use update instead of find.
router.route('/car')
.put(function(req, res, next) {
Car.update({ _id: mongoose.Types.ObjectId(req.params.id) },req.body)
.then(function (success) {
res.json();
})
.catch(function (error) {
res.status(404).send(err);
});
});

You can use Document.set().
car.set(req.body)
try {
await car.save()
}catch(err){
console.error(e)
}

For updating exist mongoose document, Is there better way than below lines?
For the better utilization of network data and performance, You should keep in mind that, update() method updates values in the existing document while the save() method replaces the existing document with the document passed in save() method.
So if your model is large and you want to update only few number of fields then update is better option.
Hope this helps.

Related

deleteMany with Mongoose using array

I am trying to batch delete using deleteMany via Mongoose. Currently I have a few rows with check-boxes and a submit button which POSTs an array of IDs to my deleteMany endpoint like this,
router.get('/list/batchDelete', secured()).delete(function(req, res) {
Booking.deleteMany(
{
_id: {$in: [req.params.ids]},
},
function(err, rowsToDelete) {
if (!err) {
res.send(rowsToDelete);
res.redirect('/list');
} else {
res.send(err);
console.log('Error in batch delete :' + err);
}
},
);
});
I can see the req.params.ids payload.
POSThttp://localhost:8000/list/batchDelete
[HTTP/1.1 404 Not Found 34ms]
Request payload
ids%5B%5D=5e1da4db2f11682b506fc6c8&ids%5B%5D=5e1da522becbb13f24748012&ids%5B%5D=5e1da57a5c7f911db82e5731
But I keep getting Cannot POST /list/batchDelete
Please, what am I missing?
I referred to:
Mongoose Delete Many by Id
Mongoose Docs: Query.prototype.deleteMany()
UPDATE:
I added a post route like this, which now produces 200OK but in the browser JSON view and still no change to the dataset.
router.post('/list/batchDelete', function(req, res) {
const ids = req.body.ids;
res.send(ids);
res.redirect('/list');
});
Use your route like this
router.post('/list/batchDelete', async (req, res) {
const {ids} = req.body;
await Booking.deleteMany(
{
_id: {$in: ids},
})
return res.send('record deleted');
});
Use postman for api call
Call should be POST and on /list/batchDelete route
Body should contain array of ids
e.g {"ids":['id1','id2']}
This will solve your problem of deleting records.

Are there other ways to update a document with Express and Mongoose without having to delete the _id property?

When I update a document with Express and Mongoose is the best practice to just delete the _id value and then save the document? Here is an example of the code I'm using.
exports.update = function(req, res, next) {
var data = _.extend(app.locals.crewListing, req.body);
data = data.toObject();
delete data._id;
app.locals.crewListing.save(function(err, result) {
if (err) return next(err);
res.json({
message: null,
data: result
});
});
};
I don't think so. You definitely need to make sure that _id property isn't changed while updating. The best way is,
if(data._id) {
delete data._id;
}
But I would prefer updating each field of the object individually instead of using .extend() function since you know what fields you are updating.

Need to send response after forEach is done

I'm working with NodeJS + Mongoose and I'm trying to populate an array of objects and then send it to the client, but I can't do it, response is always empty because it is sent before forEach ends.
router.get('/', isAuthenticated, function(req, res) {
Order.find({ seller: req.session.passport.user }, function(err, orders) {
//handle error
var response = [];
orders.forEach(function(doc) {
doc.populate('customer', function(err, order) {
//handle error
response.push(order);
});
});
res.json(response);
});
});
Is there any way to send it after the loop has finished?
Basically, you could use any solution for async control flow management like async or promises (see laggingreflex's answer for details), but I would recommend you to use specialized Mongoose methods to populate the whole array in one MongoDB query.
The most straightforward solution is to use Query#populate method to get already populated documents:
Order.find({
seller: req.session.passport.user
}).populate('customer').exec(function(err, orders) {
//handle error
res.json(orders);
});
But if, for some reason, you can't use this method, you could call Model.populate method yourself to populate an array of already fetched docs:
Order.populate(orders, [{
path: 'customer'
}], function(err, populated) {
// ...
});
One solution is to use Promises.
var Promise = require('bluebird');
Promise.promisifyAll(Order);
router.get('/', isAuthenticated, function(req, res) {
Order.findAsync({ seller: req.session.passport.user })
.then(function(orders) {
return Promise.all(orders.map(function(doc){
return Promise.promisify(doc.populate).bind(doc)('customer');
}));
}).then(function(orders){
// You might also wanna convert them to JSON
orders = orders.map(function(doc){ return doc.toJSON() });
res.json(orders);
}).catch(function(err){
//handle error
});
});
BlueBird's .promisifyAll creates an …Async version of all functions of an object, which saves you an extra step in configuring the initial promise. So instead of Order.find I used Order.findAsync in above example

Mongoose $addToSet return new list entrys

I have a question working with mongoose 4.0.1
I am trying to add new picture objects to an array inside a model. This is the code of my endpoint that is actually doing the job:
// Add new images
exports.pictures = function(req, res) {
Incident.findByIdAndUpdate(
req.params.id,
{$addToSet: {"pictures": {$each: req.body}}},
{new: true},
function(err, incident) {
if (err) { return handleError(res, err); }
return res.send(201).json(incident.pictures);
}
);
};
The problem: The callback object (incident) stores all information of the model which was found and updated. But I want to return only the new array entries which were created.
How can I receive the actual changes of the operation instead of the whole object that is storing the pictures array?
I solved the problem by creating a new schema for pictures and adding a reference to the incident model.
The endpoint changed as well:
Create new picture instances for a array of pictures
Find incident by id
Save the references of the picture instances to a array inside the incident
Return the id of the picture instances
var _ = require('lodash');
// Add new images
exports.pictures = function(req, res) {
Picture.create(req.body, function(err, pictures) {
if (err) { return handleError(res, err); }
Incident.findByIdAndUpdate(
req.params.id,
{$addToSet: {"pictures": {$each: pictures}}},
function(err) {
if (err) { return handleError(res, err); }
var pictureIds = _.map(pictures, '_id');
return res.status(201).json(pictureIds);
}
);
});
};

MongoDB two same records

I'm doing a project which is backed by Nodejs and MongoDB. I am quite new to MongoDB and I am lacking a clue why I quite often(almost always) get two same records in collections when I do posts. The two records only differ by ID, which are for example ObjectId("53aefb0fc68a0810504d2066") and 53aefb0fc68a0810504d2066, is this normal or am I doing something wrong? Thanks for any pointers.
Here is some node code:
server.js:
app.post("/:collection", function (req, res) {
var object = req.body;
var collection = req.params.collection;
collectionDriver.save(collection, object, function (err, docs) {
if (err) {
res.send(400, [err, object]);
} else {
res.send(201, docs);
}
});
});
collectionDriver:
save: function(collectionName, obj, callback) {
this.getCollection(collectionName, function(error, the_collection) {
if( error ) callback(error);
else {
obj.created_at = new Date();
the_collection.insert(obj, function() {
callback(null, obj);
});
}
});
},
getCollection: function(collectionName, callback) {
this.db.collection(collectionName, function(error, data) {
if (error) {
callback(error);
} else {
callback(null, data);
}
});
},
Everytime you ask MongoDB to save an object without an _id field, it automatically generates a new, globally unique ObjectID for that field and saves the object under that ObjectID.
When you want to use save to update an existing document, you need to make sure that the _id field is populated with the _id of the document you want to update.
Alternatively, you can create an unique index on those fields you consider relevant for determining what's a duplicate and what isn't. However, in that case an attempt to save an already existing document will throw an error instead of replacing the document.

Resources