I'm trying to update a document using mongoose. And I would like to update multiple properties at once. I've tried using the spread operator to clone the document and override the properties that need to be updated with user input, but it returned error document.save is not a function. I kind of understand why it throws the errors. Because it's not the same object, it's just a clone so it doesn't have save method. Please correct me if I'm wrong.
So my question is: is there a way to update the object like with spread operator?
My code:
router.put("/posts/update", (req, res)=>{
const {id, updatedFields} = req.body;
Post.findById(id).exec((err, post)=>{
if(err) throw err;
post = {...post, updatedFields};
post.save();
res.json(post);
})
});
You can use Object.assign
post= Object.assign(post, updatedFields);
The assign function can take as many arguments as you would like. Each argument is applied on the leftmost, from left to right. So if multiple arguments hold the same property, its value would be the one from the right.
Use Object.assing it'll not change object reference.
I would suggest instate of throwing err, send the response with some status code.
router.put("/posts/update", (req, res) => {
const {
id,
updatedFields
} = req.body;
Post.findById(id).exec((err, post) => {
if (err) return res.status(400).json({err, msg: 'Some msg'});
post = Object.assign(post, updatedFields);
post.save();
res.json(post);
})
});
Related
I have to receive an array of Objects and update each one of those, by their _id, in my MongoDB database.
I had one method that would insert one Object whenever I made an HTTP PUT, like this:
router.post('/bet', function(req, res){
Bet.update(req.body)
.then(dados => res.status(201).jsonp({dados: dados}))
.catch(e => res.status(500).jsonp({error: 'erro'}))
})
Where update() is defined as:
module.exports.update = function(d){
return Bet.findByIdAndUpdate({_id: d._id}, d, {new: true})
}
This works just fine whenever I need to update a single Bet, but to be able to update several, I created this:
router.put('/bets', function(req, res){
req.body.forEach((obj) =>{
Bet.update(obj)
.then(dados => res.status(201).jsonp({dados: dados}))
.catch(e => res.status(500).jsonp({error: e}))
})
})
Is this correct? I'm having a lot of problems, that I honestly think are coming from doing a lot of different requests at the same time, but I'd like to start by guaranteeing that this first step is done properly.
No, this is likely not the correct way. You are sending a response to the client the moment a single update is done.
I'd add a updateMany method like so:
module.exports.updateMany = function(arr){
return Promise.all(arr.map(d => Bet.findByIdAndUpdate(d._id, d, {new: true})))
}
The method uses Array#map() to create an array of Promises, that resolve once the update is done and then uses Promise.all() to give the ability to wait for all promises to be done.
It resolves once all updates are done, or rejects when any update fails.
(Note: you don't have to specify a query object like {__id:id} for findByIdAndUpdate())
You can then call that from your bets route:
router.put('/bets', function(req, res){
Bet.updateMany(req.body)
.then(promiseArray => res.status(201).jsonp({dados: promiseArray}))
.catch(e => res.status(500).jsonp({error: e}))
})
Hi fellow developers!
I've got this error showing up in my console when I try to save two identical documents in a collection in MongoDB that has nothing to do with the index shown in the error.
Here's the error: E11000 duplicate key error collection: Bohemian.orders index: user.email_1 dup key: { user.email: null }
Now this makes no sense, because I'm trying to save an Order document in a separate collection, which has nothing to do with the user router I had set up previously.
Here is the schema and model code:
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema({
amountToPay: Number,
});
const Order = mongoose.model("Order", orderSchema);
module.exports.Order = Order;
As shown here, I am only trying to save the amount to be payed into the database in a separate collection.
Here is the router file:
const express = require('express');
const router = express.Router();
const { Order } = require('../models/Order');
router.get("/", async (req, res, next) => {
try {
const orders = await Order.find();
if (orders.length === 0) return res.status(404).send("There are currently no orders");
res.send(orders);
} catch (ex) {
console.error(ex);
next();
}
});
router.post("/", async (req, res, next) => {
try {
const order = new Order({
amountToPay: req.body.amountToPay
});
await order.save();
res.send(order);
} catch (ex) {
console.error(ex);
next();
}
});
module.exports = router;
As you can see there is nothing relative to the error that I'm getting and I have no clue why I'm getting a duplicate user.email = null key , when I haven't made any reference to the User model or router.
Here is the POST call I'm making from POSTMAN to test:
Pretty straight forward, nothing extreme, nothing tangled, right? Well the first ever POST call saves the document in the Database, but from then on I keep getting the same error. The only thing I can take from that is that when I save the first document, Mongo looks for the user.email property when I'm creating the new instance of Order and when it doesnt find it, it creates it with a value of null and then the next document would naturally be a duplicate, hence the error. But I'm confused, because this model and router should not absolutely nothing to do with the user ones.
Here is the error:
So please if anyone can help me understand why MongoDB is screwing with me or where I'm making a mistake, I would really appreciate it.
I found out what the problems was, thanks to Molda:
At some point I had created these indexes, which I'm still unsure when and how, but I did. Which essentially lead to this error, when I tried to save the second document.
A simple quick console log of the indexes in the collection showed that I had that index in there.
const indexes = await Order.collection.getIndexes();
console.log(indexes);
Then I removed them using this method:
await Order.collection.dropIndexes("user.email_1");
And everything worked flawlessly from there.
I hope this helps anyone in this situation in the future and thanks Molda! :)
I am a little stumped on how to handle this the best way possible. I've decided to rewrite this controller, and I need to (at least I think) make use of promise.all() here.
Premise:
In this application, the Admin user must be able to bulk upload a bunch of .pdf's at once that are for multiple users. The .pdf's adhere to a specific naming convention that my backend upload controller by using a regEx, pulls out a first and last name. These .pdf's are auto-generated in a program, that always names them exactly the same, so there is no human error in misspelling names.
Each call to the database and an AWS S3 Bucket is made within an Array.prototype.map() a function that is looping through and uploading a file to an S3 bucket, and then it takes the Key name of the file returned from s3.upload() and saves that Key to a user model in Mongo DB as a reference to their file(s) within the S3 Bucket.
Example Code:
This is what I currently have (that does work somewhat). This is the block of code responsible for what I described above. employeeFiles is created further up in the controller and contains an array of objects that each have a file and id property. The file name destructuring and user matching happen further up in the controller as well, and the employeeFiles array is a result of that. The id property contains the mongo _id of the employee, and the file property contains the file to be saved. This all works perfectly, and I don't think that code is needed for context here. fileType is a variable available within the scope of the controller:
const employeeFileUploadToDb = () => {
employeeFiles.map((employee, i) => {
const { file, id } = employee;
const params = {
Bucket: S3_BUCKET_NAME,
Body: file.buffer,
Key: `${filetype}/${file.originalname}`
};
s3.upload(params, (err, data) => {
if (err) {
next(err);
}
if (data) {
//Save reference to Employee model
let dataObj = {
key: data.key,
fileName: file.originalname,
date: Date.now()
};
Employee.findOneAndUpdate(
{ _id: id },
{ $push: { [`${filetype}`]: dataObj } }
)
.then(resp => res.send(200))
.catch(err => next(err));
}
});
});
};
I am making use of next() to handle any errors within the s3.upload() and findOneAndUpdate() functions (I do realize findOneAndUpdate() is deprecated) moving forward. My idea here is that if there is an error with one of the functions, next() will send it to my error handler middleware and keep going, versus ending the process and halting all of it.
Inside of every iteration of s3.upload(), I make a call to my database so that I can save the reference to the file uploaded to the S3 Bucket. Inside of a then() method of Employee.findOneAndUpdate(), I return a (200) response to let my client know everything has been uploaded to S3 and saved in my DB. So on each iteration of this map() function, I am returning a 200. If I have 10 files, I am returning 200 10 times.
I feel that I can convert this into an async function, and make use of a promise.all() to return a single status code upon completion. Returning that many status codes seem a bit crazy to me. But I am not too sure how to approach this while using a map() function to loop and make an async call on every iteration.
Hope this makes sense, and thank you in advance for looking at this!
I would split it up into a 2-step process. Upload in bulk and then save to mongo if it all worked out.
const employeeFileUploadToDb = () => {
const uploadFiles = files => files.map((employee, i) => new Promise((resolve, reject) => {
//...
s3.upload(params, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
})
});
});
Promise.all(uploadData(employeeFiles)).then((err, data) => {
// Handle saving to mongo
})
};
I've been trying to figure out why my HTTP Put has not been working after I use it once. When I click a button, I push the current user's id into an array like so:
$scope.currentUser = {
'eventsAttending' = [];
}
$scope.attending = function(event, id){
if($cookieStore.get('token')){
$scope.currentUser = Auth.getCurrentUser();
}
$scope.currentUser.eventsAttending.push(event._id);
$http.put('/api/users/' + $scope.currentUser._id, $scope.currentUser)
.success(function(data){
console.log("Success. User " + $scope.currentUser.name);
});
}
And my HTTP Put function is like so:
var express = require('express');
var controller = require('./user.controller');
var config = require('../../config/environment');
var auth = require('../../auth/auth.service');
router.get('/:id', controller.getEvents);
var router = express.Router();
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
User.findById(req.params.id, function (err, user) {
if (err) { return res.send(500, err); }
if(!user) { return res.send(404); }
var updated = _.merge(user, req.body);
updated.markModified('eventsAttending');
updated.save(function (err) {
if (err) { return res.send(500, err); }
return res.json(200, user);
});
});
};
In my HTML page I have multiple events I can attend, and each event has the button called Attend where I call $scope.attending and the function is called and the HTTP Put occurs. All of this works for the first event I choose to attend. However, when I click the Attend button for another event, I get an error that says:
{"message":"No matching document found.","name":"VersionError"}
And I have no idea why. The error occurs when I try to do updated.save() in the mongoose call and I get res.send(500, err)
I tried to look at http://aaronheckmann.blogspot.com/2012/06/mongoose-v3-part-1-versioning.html to solve the issue as I did some googling but I keep getting an error that says:
Undefined type at `versionKey`
Did you try nesting Schemas? You can only nest using refs or arrays.
Upon adding into my schema:
var UserSchema = new Schema({
versionKey: 'myVersionKey',
...
I also tried to change the .save() function into .update() as someone suggested it online but that seemed to give me even more errors. Any ideas how to fix this? That would be much appreciated!
I think the issue you may be experiencing (as was the case when I was getting this error from a similar action) is that after the first update, the '__v' VersionKey property on the newly updated document has changed, but you might not be updating that property on the object you have in the browser. So when you go to update it again, you're sending the old '__v' VersionKey, (even though you updated the 'eventsAttending' property) and that document conflicts the newer VersionKey. This would assume that the Auth.getCurrentUser(); function returns the whole document object from mongo.
What I did to fix this was simply add delete entity.__v; to delete the old VersionKey from the document before sending it with the request. Better yet, I'd recommend updating the properties your API sends back when returning documents so this issue doesn't happen in the first place.
I'm having issues updating a document from within a find using Mongoose. The issue is only when I attempt to overwrite the document with an object (e.g doc = req.body). I am however able to directly set properties of the original document to a specific string (e.g. doc.name = 'jason borne';).
I've verified that res.body is an object, so I don't see why I'm unable to set it this way.
Client.findById(req.params.client_id, function (err, client) {
if (err)
return next(new restify.InternalError(err));
// client.name = 'jason borne';
client = req.body;
client.save(function(err) {
if (err)
return next(new restify.InternalError(err));
res.send(client);
});
});
When attempting to set the doc to an object, I receive the error:
TypeError: Object # has no method 'save'
I'm aware that I can do an update with a simple Client.update(...) command, however this method does not allow my schema middleware or validation to run (which is notated in the Mongoose documentation).
Any thoughts? I'm new to Node, and Mongoose.
You need to use something like underscore's extend method to copy the properties of req.body into the client object instead of just re-pointing client to req.body as you are now.
var _ = require('underscore');
Client.findById(req.params.client_id, function (err, client) {
if (err)
return next(new restify.InternalError(err));
_.extend(client, req.body);
client.save(function(err) {
if (err)
return next(new restify.InternalError(err));
res.send(client);
});
});
The symptoms you're now getting are caused by the fact that you're replacing a mogoose model object (with methods like save, find, etc), by a simple json object parsed from your body, which is missing save.
Try doing an update instead of find/save.
Client.update({_id: req.params.client_id}, {$set : req.body}, function(err) {...});
Or try to merge your req.body to the client object.