How to update 2 collections with one request in mongoDB? - node.js

I have the following route using express and mongoose that update 2 collections from one route. The updates work and are reflected in MongoDb, however, at the end of the request the server crashes with the error: code: 'ERR_HTTP_HEADERS_SENT'. Is there a way for me to avoid this error?
router.post("/user-adopted", verify, (req, res) => {
const userId = req.body.userId;
const petId = req.body.petId;
User.findOneAndUpdate(
{ _id: userId },
{ adoptedPet: petId, petId: false, adoptionRequest: false },
function (err, result) {
if..else..
}
);
Data.findOneAndUpdate(
{ _id: petId },
{ adopted: true },
function (err, result) {
if...else..
);
});

As soon as the User record is updated it performs the operation you have defined in the callback and Data record is left out so to prevent that do the updation of Data record in the callback of user and try to use updateOne() instead of findOneAndUpdate() :
User.updateOne(
{ _id: userId },
{ adoptedPet: petId, petId: false, adoptionRequest: false },
function (err, result) {
if(err) res.send(err)
Data.updateOne(
{ _id: petId },
{ adopted: true },
function (err, result) {
if(err)
res.send(err)
else{
// Redirect where you want to go
}
}
);
}
);

Related

findOneAndUpdate mongoose; Cannot set headers after they are sent to the client

After a record is created, I'm trying to use $push to send the ID for the record that was just created into a different model.
likeRoutes.route('/add').post(function(req, res){
let like = new Like({
value: req.body.value,
_report: req.body._report
})
like.save((err, doc) => {
if (err)
res.send(err)
console.log('in router - card id', req.body._report)
console.log('doc id', doc)
Report.findOneAndUpdate({ _id: req.body._report },
{ $push: { like: doc._id } },
{ new: true , useFindAndModify: false },
(err, post) => {
if (err)
res.send(err)
res.json({doc})
}
)
})
})
When I run this, I keep getting the error of Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I previously had it configured like this:
Report.findOneAndUpdate({ _id: req.body._report },
And it was adding the like record to the same report everytime, and did not look up by ID.
You can't call res.send and res.json one after the other.
Try this:
likeRoutes.route('/add').post(function(req, res){
let like = new Like({
value: req.body.value,
_report: req.body._report
})
like.save((err, doc) => {
if (err) return res.send(err)
console.log('in router - card id', req.body._report);
console.log('doc id', doc);
Report.findOneAndUpdate({ _id: req.body._report },
{ $push: { like: doc._id } },
{ new: true , useFindAndModify: false },
(err, post) => {
if (err) return res.send(err);
res.json({doc})
}
)
})
})
Now in case of an error you are stop execution and return the error. If no error - you are sending the result.

'findOneAndUpdate()' does not update the existing data in mongo db

I am developing a web application using MEAN Stack with Angular 6. There if the user previously has added data into the db that data should be updated. For that I used findOneAndUpdate() method. But without updating the existing data it posts another data set into the db.
This is my post route. This 'userName' comes from a different schema.
router.post('/save', function(req, res) {
var mod = new rdaColor(req.body);
rdaColor.findOneAndUpdate(
{
userName: req.body.userName,
colorMovementBox: req.body.colorMovementBox
},
req.body,
{ upsert: true, new: true },
function(err, data) {
if (err) {
console.log(err);
res.send(err);
} else {
res.send(mod);
}
}
);
});
This is schema.
var mongoose = require('mongoose');
// Schema for rda color panel
var rdaColorSchema = new mongoose.Schema({
userName: {
type: String
},
colorMovementBox: {
type: String,
},
});
module.exports = mongoose.model('rdaColor', rdaColorSchema);
This is the output for the following console.log.
console.log("mod"+mod+" "+(req.body));
output
mod{ _id: 5bbd68344619a612b07a688e,
userName: 'abc#yahoo.com',
colorMovementBox: 'rgb(49,64,116)',
} [object Object]
How can I make it only to update the data.
Please check this query.
This will update if data exists with Id and create new if not exists.
If you remove upsert: true then it does not create a new record if not exists.
rdaColor.findOneAndUpdate(
{ _id: "yourId" },
{
$set: {
userName: req.body.userName,
colorMovementBox: req.body.colorMovementBox
}
},
{ upsert: true, new: true },
function(err, doc) {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
}
);

How to delete Element In MongoDB property's array with MongooseJS?

I cannot remove an element inside of an array that is a property of a MongoDB Model.
Please remember this is a NodeJS module mongooseJS and not the real MongoDB so functionalities are not the same..
GOAL: Delete an object from the statusLiked array. | I have also confirmed that the value of status.id is correct.
Model:
Const userSchema = new mongoose.Schema({
myStatus: Array,
statusLiked: Array,
)};
Delete:
1. Deletes the status(works). 2. Delete the status from User.statusLiked(no work).
exports.deleteStatus = (req, res, next) => {
var CurrentPost = req.body.statusid; // sends in the status.id
Status.remove({ _id: CurrentPost }, (err) => {
if (err) { return next(err); }
// vvvv this vvv
User.update( {id: req.user.id}, { $pullAll: {_id: CurrentPost }, function(err) { console.log('error: '+err) } });
req.flash('success', { msg: 'Status deleted.' });
res.redirect('/');
});
};
What happens: The specific status(object) is deleted from the database. But the status still remains in the User.statusLiked array.
What I want to happen: Status to be deleted from the User.statusLiked array and the status to be deleted from the database. Then, reload the page and display a notification.
I got it to work somehow. Working code:
exports.deleteStatus = (req, res, next) => {
var CurrUser = req.body.userid;
var CurrentPost = req.body.post;
Status.remove({ _id: CurrentPost }, (err) => {
if (err) { return next(err); }
console.log('meeee'+CurrentPost+'user: ' +CurrUser);
req.flash('success', { msg: 'Status deleted.' });
res.redirect('/');
});
User.update(
{ _id: new ObjectId(CurrUser)},
{ $pull: { myStatus : { _id : new ObjectId(CurrentPost) } } },
{ safe: true },
function (err, obj) {
console.log(err || obj);
});
};

How to remove object taking into account references in Mongoose Node.js?

This is my MongoDB schema:
var partnerSchema = new mongoose.Schema({
name: String,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
}]
});
var productSchema = new mongoose.Schema({
name: String,
campaign: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Campaign'
}
]
});
var campaignSchema = new mongoose.Schema({
name: String,
});
module.exports = {
Partner: mongoose.model('Partner', partnerSchema),
Product: mongoose.model('Product', productSchema),
Campaign: mongoose.model('Campaign', campaignSchema)
}
And I wondering how can I remove object from any document taking into account references (maybe should I use somehow populate from mongoose)? For example if I will remove Product then I assume that I will remove also ref ID in Partner and all Campaigns which belong to this Product.
At the moment I removing in this way:
var campSchema = require('../model/camp-schema');
router.post('/removeProduct', function (req, res) {
campSchema.Product.findOneAndRemove({ _id: req.body.productId }, function (err, response) {
if (err) throw err;
res.json(response);
});
});
However in mongo still left references.
You would have to nest your calls to remove the product id from the other model. For instance, in your call to remove the product from the Product
collection, you could also make another call to remove the ref from the Partner model within the results callback. Removing the product by default will remove its refs to the Campaign Model.
The following code shows the intuition above:
var campSchema = require('../model/camp-schema');
router.post('/removeProduct', function (req, res) {
campSchema.Product.findOneAndRemove({ _id: req.body.productId }, function (err, response) {
if (err) throw err;
campSchema.Partner.update(
{ "products": req.body.productId },
{ "$pull": { "products": req.body.productId } },
function (err, res){
if (err) throw err;
res.json(res);
}
);
});
});
To remove the associated campaigns then you may need an extra remove operation that takes in the associated campaign id fro a given product id. Consider the following dirty hack which may potentially award you a one-way ticket to callback hell if not careful with the callback nesting:
router.post('/removeProduct', function (req, res) {
campSchema.Product.findOneAndRemove(
{ _id: req.body.productId },
{ new: true },
function (err, product) {
if (err) throw err;
campSchema.Partner.update(
{ "products": req.body.productId },
{ "$pull": { "products": req.body.productId } },
function (err, res){
if (err) throw err;
var campaignList = product.campaign
campSchema.Campaign.remove({ "_id": { "$in": campaignList } })
.exec(function (err, res){
if (err) throw err;
res.json(product);
})
}
);
}
);
});
Although it works, the above potential pitfall can be avoided by using async/await or the async library. But firstly, to give you a better understanding of the using multiple callbacks with the async module, let's illustrate this with an example from Seven Things You Should Stop Doing with Node.js of multiple operations with callbacks to find a parent entity, then find child entities that belong to the parent:
methodA(function(a){
methodB(function(b){
methodC(function(c){
methodD(function(d){
// Final callback code
})
})
})
})
With async/await, your calls will be restructured structured as
router.post('/removeProduct', async (req, res) => {
try {
const product = await campSchema.Product.findOneAndRemove(
{ _id: req.body.productId },
{ new: true }
)
await campSchema.Partner.update(
{ "products": req.body.productId },
{ "$pull": { "products": req.body.productId } }
)
await campSchema.Campaign.remove({ "_id": { "$in": product.campaign } })
res.json(product)
} catch(err) {
throw err
}
})
With the async module, you can either use the series method to address the use of callbacks for nesting code of multiple methods which may result in Callback Hell:
Series:
async.series([
function(callback){
// code a
callback(null, 'a')
},
function(callback){
// code b
callback(null, 'b')
},
function(callback){
// code c
callback(null, 'c')
},
function(callback){
// code d
callback(null, 'd')
}],
// optional callback
function(err, results){
// results is ['a', 'b', 'c', 'd']
// final callback code
}
)
Or the waterfall:
async.waterfall([
function(callback){
// code a
callback(null, 'a', 'b')
},
function(arg1, arg2, callback){
// arg1 is equals 'a' and arg2 is 'b'
// Code c
callback(null, 'c')
},
function(arg1, callback){
// arg1 is 'c'
// code d
callback(null, 'd');
}], function (err, result) {
// result is 'd'
}
)
Now going back to your code, using the async waterfall method you could then restructure your code to
router.post('/removeProduct', function (req, res) {
async.waterfall([
function (callback) {
// code a: Remove Product
campSchema.Product.findOneAndRemove(
{ _id: req.body.productId },
function (err, product) {
if (err) callback(err);
callback(null, product);
}
);
},
function (doc, callback) {
// code b: Remove associated campaigns
var campaignList = doc.campaign;
campSchema.Campaign
.remove({ "_id": { "$in": campaignList } })
.exec(function (err, res) {
if (err) callback(err);
callback(null, doc);
}
);
},
function (doc, callback) {
// code c: Remove related partner
campSchema.Partner.update(
{ "products": doc._id },
{ "$pull": { "products": doc._id } },
function (err, res) {
if (err) callback(err);
callback(null, doc);
}
);
}
], function (err, result) {
if (err) throw err;
res.json(result); // OUTPUT OK
});
});

Cannot update MongoDB using mongoose

I am trying to update a collection from my database using de node module mongoose. The problem is with $set updates. Here is my code:
// Update a user
app.patch('/user/:user_id', passport.authenticate('bearer', { session: false }),
function (req, res) {
var conditions = { _id: new ObjectId(req.params.user_id)},
updateObj = { $set: req.body }; // {email : "bob#example.com", username: "bob"}
User.update(conditions, updateObj, function callback (err, numAffected, rawResponse) {
if (err) {
res.send(err);
return;
}
// numAffected is the number of updated documents
if (numAffected == 0) {
res.json({ message: 'No user affected'});
return;
}
res.json({ message: 'User updated'});
});
});
If I update an existing key like email, it is updated. But if I want to add a new key, numAffected is always 0 and the rawResponse is undefined.
Any idea of what happens?
Edit
Here is my Schema:
var userSchema = mongoose.Schema({
email : String,
username : String,
password : String
});
In order to set multiple fields in a document, you must set the Multi option in your config, otherwise Mongoose will ignore the continuation, and only update the first doc.
From the docs:
var conditions = { name: 'borne' }
, update = { $inc: { visits: 1 }}
, options = { multi: true };
Model.update(conditions, update, options, callback);
function callback (err, numAffected) {
// numAffected is the number of updated documents
});
Another note here: The numAffected should return as expected, but I can't find any documentation on their site about the raw response, but it should return as expected as well. Do you know of any documentation for this?
I think this is what you really want to do with mongoose to update email and username of a user.
app.patch('/user/:user_id', passport.authenticate('bearer', { session: false }),
function (req, res) {
User.findOneAndUpdate({_id: req.params.user_id},
{
$set: {
username: req.body.username,
email: req.body.email
}
}, function(err, user) {
if (err)
res.send(err);
if (user) {
res.json({message: 'User updated'});
} else {
res.json({message: 'User does not exist'});
}
});
});

Resources