Pre-populating documents using Mongoose + Express - node.js

I am new to Node+Mongoose, and am currently in the process of creating an API using KeystoneJS. I've managed to populate all posts with the author's email and name. My question is, is there a way to populate the post with author everytime, possibly with some middlewear, without having to rewrite it in each method where I retrieve posts? My goal is to not have multiple instances of populate('author', 'email name') scattered throughout the code. For instance, in the future, I'd like to include the author's profile photo url as well, and I'd prefer to be able to make that change in a single place, that will then be reflected in every place that I retrieve posts.
Current implementation:
Post.model.find().populate('author', 'email name').exec(function (err, posts) {
if (!err) {
return res.json(posts);
} else {
return res.status(500).send("Error:<br><br>" + JSON.stringify(err));
}
});
Post.model.findById(req.params.id).populate('author', 'email name').exec(function (err, post) {
if(!err) {
if (post) {
return res.json(post);
} else {
return res.json({ error: 'Not found' });
}
} else {
return res.status(500).send("Error:<br><br>" + JSON.stringify(err));
}
});

You can create model using statics. This is example of methods for schema
PostSchema.statics = {
getAll: function(cb) {
return this
.find()
.populate('author', 'email name')
.exec(cb);
}
}
You still should use "populate", but it will be in schema file, so you will not care about it in future

Related

I want to check another field When I get dublicate key error for email (unique) in mongodb findoneandupdate

User.findOneAndUpdate({ _id: userFindByid }, {
$set: {
"email": req.body.email,
"username": req.body.username,
"phone_number": req.body.phone_number,
"address": req.body.address,
"isBenefactor": req.body.isBenefactor,
"location": req.body.location
}
}, { runValidators: true, context: 'query' }, (err, doc) => {
if (err) {
// if request email has already exist in db I want to check that emails isDeleted field in here . if isDeleted is true I want to update .
return res.status(500).json({ message: err.message });
}
else {
return res.status(200).json({ message: 'Your account was updated' });
}
})
//
Let me explain scenario clearly,
I registered with an email address(first#gmail.com) then I deleted my account =>(first#gmail.com)=>isDeleted=true
After that I again registered with another email address(second#gmail.com)=>isDeleted=false
Now I want to update my second email address with first one I will get an unique key error because (first#gmail.com) is in mydb ,but I have to da update process because (first#gmail.com)=>IsDelete=true
If I use { 'email': req.body.email, 'isDeleted': true} I can not update (second#gmail.com)=>isDeleted=false
I can fix the problem by using too much if statements , but I dont want to use if statements too much. I am looking for best practice for that problem.
I hope I could explain
Here is my code block , can someone help me ?
THIS ANSWER ASSUMES YOU ARE USING MONGOOSE!
One way you can do is instead of using findOneAndUpdate you can use .save this way you can issue a hook on mongoose.
For example, you would do User.save(...) then you go to your schema code and you add the following (assuming your schema name is UserSchema)
UserSchema.post('save', function(error, doc, next) {
// Error code 11000 means this is a duplicate
if (error.name === 'MongoError' && error.code === 11000) {
// So instead of throwing an error you would do anything you want
// Such as look for the other record and delete it, update its isDelete
// field, remove email, etc... really is up to you
}
next()
});
EDIT: Of course before User.save(...) you need to find the user!
For example,
User.findOne({_id:1}, function(err, doc){
// Update doc values
// Finally do doc.save(...)
})
You can make it look much better by using the async library and using async.waterfall
EDIT2: Okay so now that I understand your requirement better, here is your best solution in my opinion.
Find the user you want to update
Change email
Save
On the other side, you need to have a hook (unfortunately its poorly documented, you have to do your own digging but here is a link to mongoose documentation)
Here is how this will work
1. Hook a pre save (before actually saving execute specific block of code)
2. The block of code will only execute when the email is modified (we dont want to execute it everytime, its just a waste of resources)
3. The block of code will use deleteOne and delete the user matching that email
NOTE: For best performance make sure to index the email and make it unique!
I have created the full (similar to what you want) project with the code on here
But if you wish here are also some snippet
// This will run before saving the object
UserSchema.pre('save', function (next) {
let user = this
// Make sure to run only when email is modified
if(user.isModified('email')) {
// IF the email was modified, then attempt to delete a record with this email (if there is one, then it will be deleted otherwise it will just continue)
this.constructor.deleteOne({email:this.email, isDeleted:true}, (err) => {
err ? next(err) : next()
})
} else {
next()
}
})
// Code to save/update the user
User.findOne({_id:"1"}, function(err, user) {
if (err) {
throw err
} else {
if (user) {
user.email = "test2#test.com"
user.save(err => {
err ? console.log(err) : console.log("Success!")
})
} else {
console.log("User was not found!")
}
}
})
Good luck!

Mongoose not saving document completely

I am trying to save data to my mongo db using mongoose. But unfortunately I am not able to save it completely. However it creates a data array but defaults like company name etc. are not saving. However these values are not available in requested body
I am using:
var seller = new sellers(req.body);
seller.save(function (err) {
if (err) {
return res.status(500).send('An user with this email id or mobile number already exist');
}
res.status(200).send('You have successfully registered');
})
In this case you could use a pre save hook to set an object as the default in your array:
userSchema.pre('save', function(next) {
if (this.data.length == 0) {
var default = {
fieldName: 'Company Name',
fieldValue: 'No Information Provided',
// etc.
};
this.data.push(default);
}
next();
});

MongoDB check for existing value

How do I check if a mongo/mongoose value exists before continuing in a post route?
My (partial) Mongoose model looks like this:
reserved: {type: Boolean, default: false}
module.exports = mongoose.model("Rentals", rentalsSchema);
I've been trying to check to see if reserved is true in this way without any luck
NOTE: As you'll see below this needs to work for each rentals ID so it will need to findById somehow.
router.put("/:id/reserve", middleware.checkRentalsStatus, function(req, res){
//checking here for existing value
//checking here for existing value
if(Rentals.reserved == true){
res.redirect("back");
console.log('TAKEN!')
} else {
// Token is created using Stripe.js or Checkout!
// Get the payment token submitted by the form:
var token = req.body.stripeToken; // Using Express
// Charge the user's card:
var charge = stripe.charges.create({
amount: 19500,
currency: "usd",
description: "",
source: token,
}, function(err, charge) {
if(err){
req.flash("error", err.message);
res.redirect("back");
} else {
var reservedby = req.body.reservedby;
var reserved = true;
var reservedemail = req.body.reservedemail;
var newReservation = {reserved: reserved, reservedby: reservedby, reservedemail: reservedemail }
Rentals.findByIdAndUpdate(req.params.id, {$set: newReservation}, function(err, rentals){
if(err){
req.flash("error", err.message);
res.redirect("back");
} else {
req.flash("success","It's reserved for you. Thank you!");
res.redirect("/rentals/" + rentals._id);
}
});
}
});
console.log('charged')
});
How would I change my code to check if reserved is true prior to finishing the post route? (I'm trying to check if a rental is available on a reserve route in the back end. )
you can directly check if/else condition like this
if(Rentals.reserved )
{ res.redirect("back"); console.log('TAKEN!') }
else { // continue with post route
}
This is the answer. Best way is to create a middleware to check. The issue with the other answers is they do not find the renal's ID. Rentals.findByID is the key to making this work. :)
//CHECK Rentals HAS NOT BEEN RENTED
middlewareObj.checkRentalsStatus = function(req, res, next){
Rentals.findById(req.params.id, function(err, foundRentals){
if (err){
res.redirect("back");
}
if (foundRentals.reserved){
req.flash("error", 'Oh no! It looks like someone else JUST rented this location. Your credit card was not charged.');
res.redirect("back");
} else {
next();
}
});
}

Why is my Model.find returning nothing?

I'm very new to node and sequelize and I'm trying to follow this short introduction.
I've worked through the parts to connect to my database (postgres). I've also defined a model:
var User = sequelize.define('User', {
username: Sequelize.STRING,
password: Sequelize.STRING
});
I have succesfully synchronized the scheme and created instances. But when I attempt to read from the database using this:
User
.find({ where: { username: 'john-doe' } })
.then(function(err, johnDoe) {
if (!johnDoe) {
console.log('No user with the username "john-doe" has been found.');
} else {
console.log('Hello ' + johnDoe.username + '!');
console.log('All attributes of john:', johnDoe.get());
}
});
That instance does exist, but I only ever see the 'No user with...' message. The query it generates seems correct and when I try it manually, the returned results are what I would expect to see.
Using the same query I can do this, which also works:
sequelize.query("SELECT * FROM my_user_table where username='john-doe'", { type: sequelize.QueryTypes.SELECT})
.then(function(items) {
// We don't need spread here, since only the results will be returned for select queries
console.log(items);
});
What am I missing here?
You're mixing up promises and node-style callbacks. Typically you only expect (err, results) when you pass a callback to the original function. If you call then, you are working with promises and should only expect results. You should call catch to get any errors.
User
.find({ where: { username: 'john-doe' } })
.then(function(johnDoe) {
if (!johnDoe) {
console.log('No user with the username "john-doe" has been found.');
} else {
console.log('Hello ' + johnDoe.username + '!');
console.log('All attributes of john:', johnDoe.get());
}
})
.catch(function(err) {
// Error handling here
});
Actually, you was too close. But you must not use an argument for error handling on then method.
So you must use like the following;
User
.findOne({ where: { username: 'john-doe' } })
.then(function(johnDoe) {
if (!johnDoe) {
console.log('No user with the username "john-doe" has been found.');
} else {
console.log('Hello ' + johnDoe.username + '!');
console.log('All attributes of john:', johnDoe.get());
}
});

Checking whether a document present in mongodb using mongoose

I am trying to create a nodejs application using mongodb as database. I need to check whether a username exist in mongodb or not. If username present, it will output "username not available", else will list all the user details with that username.
var userSchema = require('../schemas/user');
exports.collect = function(req,res) {
userSchema.find({ username: "bob" }).exec(function(err,display) {
if(err){
console.log(err);
}
else
{
if(display=='[]'){
res.send("username not available");
}
else{
res.send(display)
}
}
});
};
Is there any alternative or simple way for performing this operation?
I would decouple the whole logic from the controller if you ask me, but I'm not going to lecture you on that. I would use findOne as that will find just one record. If the user is not available, you will know that this username is not available. Don't forget that even when an error occurs, you still want to output "some" data, as you don't want the client to wait until it times out.
var userSchema = require('../schemas/user');
exports.collect = function(req,res) {
userSchema.findOne({username: "bob"}).exec(function(err, user) {
if (err) {
console.log(err);
// Handle the error properly here, we should not continue!
return res.sendStatus(500);
}
if (!user) {
return res.send("username not available");
}
// Don't know what you want to do with it, I just display it like this
return res.json(user);
});
};

Resources