Mongoose objectId is not saving in another collection - node.js

I am new to mongoose and nodejs. Please help me to understand what is wrong in my code while saving collection. My user.js looks like below
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new Schema ({
name: { type: String, required: true },
username:{ type:String, required: true, index:{ unique: true}},
password:{ type: String, required: true, select: false},
email:{ type: String, required: true, select: true},
mobile:{ type: String, required: true, select: true},
nativecid:{ type: Schema.Types.ObjectId, ref:'City'},
regdate:{ type: Date, default: Date.now },
did:{ type: String, required: false }
});
UserSchema.pre('save', function(next){
var user = this;
if(!user.isModified('password')) return next();
bcrypt.hash(user.password, null, null, function(err, hash){
if(err) return next(err);
user.password = hash;
next();
});
});
UserSchema.methods.comparePassword = function(password){
var user = this;
return bcrypt.compareSync(password, user.password);
};
module.exports = mongoose.model('User', UserSchema);
And my City model ie city.js looks like below
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CitySchema = new Schema({
name: { type: String, required: true },
status: { type: Boolean, default: true },
date: { type: Date, default: Date.now }
});
module.exports = mongoose.model( 'City', CitySchema );
City is already stored in database as below
{ "_id" : ObjectId("56a4a0adb0f445561cfd4e37"), "name" : "New York", "date" : ISODate("2016-01-24T10:00:13.220Z"), "status" : 1, "__v" : 0 }
I want this ObjectId of New York should be saved in user collection while user signups. I am trying like below but not succeed.
var User = require('../models/user');
var City = require('../models/city');
api.post('/signup', function(req, res){
var user = new User({
name: req.body.name,
username: req.body.username,
password: req.body.password,
email: req.body.email,
mobile: req.body.mobile
});
City
.findOne({ name: "New York" })
.populate('_id')
.exec(function (err, city1) {
if(err){
res.send(err);
return;
}
console.log('The creator is %s', city1.name);
return user.nativecid = city1._id;
})
user.save(function(err){
if(err){
res.send(err);
return;
}
res.json({
success: true,
message: 'User created successfully!'
});
});
});
I am not understanding what is wrong. Please advice me.

Just save user in city.findOne (nested) and may be no need to populate you should use projection.
City.findOne({ name: "New York" }, {name:1}) // ID return defaulat
.exec(function (err, city1) {
if(err){
res.send(err);
return;
}
console.log('The creator is %s', city1.name);
user.nativecid = city1._id;
user.save(function(err1){
if(err1){
res.send(err1);
return;
}
res.json({
success: true,
message: 'User created successfully!'
});
});
})

Related

MongoDB/Mongoose Populate

Working with Mongoose "Populate" - So far I'm unable to successfully get the "Food" model to populate the "User" model.
The goal is to be able to save a "Food" to a user.
USER MODEL:
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId}],
easy: {type: Boolean, default: false},
});
UserSchema.plugin(passportLocalMongoose)
module.exports = mongoose.model("User", UserSchema);
FOOD MODEL:
var foodSchema = new mongoose.Schema({
name: { type: String, required: false, unique: true },
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
}
});
module.exports = mongoose.model("Food", foodSchema);
GET ROUTE
router.get("/dashboard", function (req, res) {
User.find({currentUser: req.user})
.populate({path: 'foods'}).
exec(function (err, foods) {
if (err) return (err);
console.log('The food is:', req.user.foods.name);
});
});
POST ROUTE:
router.post("/dashboard", function(req, res, next) {
User.update({ id: req.session.passport.user }, {
}, function(err, user) {
if (err) return next(err);
User.findById(req.user._id, function(err, user) {
var newFood = new Food({
name: req.body.currentBreakfast,
image: 'test',
});
user.foods = newFood
user.save();
});
});
res.redirect('/dashboard');
});
You need to add the ref field in your user schema for foods to be populated while querying user.
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Food' }],
easy: {type: Boolean, default: false},
});
You can user this query.
await User.find({currentUser: req.user}).populate('foods')
Try this it will auto-populate data
var UserSchema = new mongoose.Schema({
username: String,
password: String,
foods: [{ type: mongoose.Schema.Types.ObjectId,ref: 'Food'}}],
easy: {type: Boolean, default: false},
});
UserSchema.pre('find', prepopulate)
function prepopulate(){
return this.populate('foods')
}

ERROR: ValidationError: CastError: Cast to ObjectID failed for value

SITUATION:
It seems I must have made a mistake in my Mongoose Model or in one of the parameters that are passed to the route.
I am fairly new to the angular2 architecture, so the mistake might be quite obvious.
ERROR:
ERROR: ValidationError: CastError: Cast to ObjectID failed for value "{ title: 'das',
username: 'John',
choice1: 'FSDAFASDF',
choice2: 'FDSAFD',
counter1: 11,
counter2: 0,
pollId: '5920598ade7567001170c810',
userId: '591c15b3ebbd170aa07cd476' }" at path "poll"
CODE:
route
router.patch('/', function (req, res, next) {
var decoded = jwt.decode(req.query.token);
User.findById(decoded.user._id, function (err, user) {
user.votes = req.body.votes;
user.save(function(err, result) {
if (err) {
console.log("ERROR: "+err);
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
res.status(201).json({
poll: 'Vote Saved',
obj: result
});
});
});
});
models/user:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var mongooseUniqueValidator = require('mongoose-unique-validator');
var schema = new Schema({
firstName: {type: String, required: true},
lastName: {type: String, required: true},
password: {type: String, required: true},
email: {type: String, required: true, unique: true},
polls: [{type: Schema.Types.ObjectId, ref: 'Poll'}],
votes: [{
poll: {type: Schema.Types.ObjectId, ref: 'Poll'},
choice: {type: Number},
}],
});
schema.plugin(mongooseUniqueValidator);
module.exports = mongoose.model('User', schema);
models/poll
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user');
var schema = new Schema({
title: {type: String, required: true},
choice1: {type: String, required: true},
choice2: {type: String, required: true},
counter1: {type: Number, required: true},
counter2: {type: Number, required: true},
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
schema.post('remove', function (poll) {
User.findById(poll.user, function (err, user) {
user.polls.pull(poll);
user.save();
});
});
module.exports = mongoose.model('Poll', schema);
EDIT:
router.patch('/', function (req, res, next) {
var decoded = jwt.decode(req.query.token);
console.log("VALID ID ? :"+mongoose.Types.ObjectId.isValid(decoded.user._id));
console.log("DECODED USER ID:"+ decoded.user._id);
User.findByIdAndUpdate(decoded.user._id, {votes: req.body.votes}, function (err, user) {
user.save(function(err, result) {
if (err) {
console.log("ERROR: "+err);
return res.status(500).json({
title: 'An error occurred',
error: err
});
}
res.status(201).json({
poll: 'Vote Saved',
obj: result
});
});
});
});
I'm thoughtfully guessing that this particular piece of code is what causes the issue:
...
User.findById(decoded.user._id, function (err, user) {
user.votes = req.body.votes;
user.save(function(err, result) {
...
mongoose is trying to resave the model and overwrite it's _id property with a plain string, whereas it should be an instance of the ObjectId.
Instead of using save to update your model, please try to use findByIdAndUpdate instead. If this is working, than my guess would be correct.
User.findByIdAndUpdate(decode.user._id, {votes: req.body.votes}, function (err, user) {
Or, cast the string _id into an ObjectId manually
...
User.findById(decoded.user._id, function (err, user) {
user.votes = req.body.votes;
user._id = mongoose.Types.ObjectId(user._id);
user.save(function(err, result) {
...
The first is preferred.

NodeJs mongoose CastError on method findById

I have a problem with mongoose, when i use the method findByIdi receive error :
CastError: Cast to ObjectId failed for value "protected" at path "_id"
My _id is valid tested by mongoose.Types.ObjectId.isValid(_id);
I also tested to convert my string _id to ObjectId: mongoose.Types.ObjectId(_id) same error...
My Model is :
var UserSchema = new Schema({
_id: {type:ObjectIdSchema, default: function () { return new ObjectId()} },
email: { type: String, unique: true, required: true },
pseudonyme: { type: String, unique: true, required: true },
password: { type: String, required: true }})
I use node v6.7.0 and mongoose v4.6.5
Thks in advance for your help,
Full Code :
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
//payload { _id: "58109f58e1bc7e3f28751cdb",email: "antoine.drian#laposte.net",exp: 1477494763,firstName: "antoine",iat: 1477484683,lastName: "drian"}
var isValid = mongoose.Types.ObjectId.isValid(payload._id);
if(!isValid) done(null, false);
var ObjectId = mongoose.Types.ObjectId;
var _id = ObjectId(payload._id);
User.findById( _id , function(err, user) {
if (err) { return done(err, false); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
models/User.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectIdSchema = Schema.ObjectId;
var ObjectId = mongoose.Types.ObjectId;
var bcrypt = require('bcrypt');
// set up a mongoose model
var UserSchema = new Schema({
// _id: {type: Schema.Types.ObjectId, auto: true},
email: { type: String, unique: true, required: true },
pseudonyme: { type: String, unique: true, required: true },
password: { type: String, required: true },
profile: {
firstName: { type: String, required: true },
lastName: { type: String, required: true },
birthdate: { type: Date },
gender: { type: String, enum: ['Male', 'Female'] },
}
}, {
timestamps: true
});
UserSchema.pre('save', function(next) {
var user = this;
if (this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
UserSchema.methods.comparePassword = function(passw, cb) {
bcrypt.compare(passw, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', UserSchema);
I found the solution it was just a route conflict between two routes with ExpressJS.
There is no relation between the both.
Thks all people for your help
Try removing the _id from your schema.
var UserSchema = new Schema({
email: { type: String, unique: true, required: true },
pseudonyme: { type: String, unique: true, required: true },
password: { type: String, required: true }
});
Try using the playload_id directly without casting it like below
User.findById( payload._id , function(err, user) {
if (err) { return done(err, false); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
first don't define _id in your Schema, and change 'isValid', use this instead
var UserSchema = new Schema({
email: { type: String, unique: true, required: true },
pseudonyme: { type: String, unique: true, required: true },
password: { type: String, required: true }
})
and if is there an error keep it as first parameter EX : done(err) otherwise use null, EX: done(null, result)
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
var _id = mongoose.mongo.ObjectId(payload._id);
User.find( {_id : _id} , function(err, user) {
if (err) { return done(err); }
if (user) {
done(null, user);
} else {
done(new Error('User not found!!!'));
}
});
});

Mongoose.populate() not showing any change in DB

I have been trying to populate from a user table and have been unsuccessful. Any help would be appreciated.
I am checking a variable isProvider
if(true)
then the data is saved in a provider table
else
in a customer table.
I want the user table to be an Auth table, so I want to populate a field called "userId" in these models. The id is being saved. When i print the results of populate, It shows a populated json but when i see it in the database it shows only the Id. I want to access the details of user table through the photographer table. How do i achieve this ?
User model
/*
* Title: User model
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
passportLocalMongoose = require('passport-local-mongoose');
var bcrypt = require('bcrypt-nodejs');
//Data model
var UserSchema = new Schema({
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
token: String,
mobile: String,
type: String,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
isPhotographer: {type: Boolean, default: false},
verified: {type: Boolean, default: false}
});
UserSchema.pre('save', function(next) {
var user = this;
if(this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function (err, salt) {
if(err) {
return next(err);
}
bcrypt.hash(user.password, salt, null, function (err, hash) {
if ( err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
UserSchema.methods.comparePassword = function (passw, cb) {
bcrypt.compare(passw, this.password, function( err, isMatch) {
if(err) {
return cb(err);
}
cb(null, isMatch);
});
};
UserSchema.plugin(passportLocalMongoose);
user = mongoose.model('User', UserSchema);
module.exports = user;
Provider model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var User = require('./User');
//Data model
var providerSchema = new Schema({
userId: {
type: Schema.Types.ObjectId,
ref: 'User'
},
firstName: String,
lastName: String,
profilePicture: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GFS'
},
email: String,
phone: Number,
address: String,
dob: Date,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
});
providerSchema.pre('save', function(next) {
this.lastModifiedOn = new Date;
next();
});
provider= mongoose.model('provider', providerSchema);
module.exports = provider;
Customer model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var User = require('./User');
//Data model
var customerSchema = new Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
firstName: String,
lastName: String,
createdOn: {type: Date, default: Date.now},
lastModifiedOn: {type: Date},
deleted: {type: Number, default: 0},
});
customerSchema.pre('save', function(next) {
this.lastModifiedOn = new Date;
next();
});
customer = mongoose.model('Customer', customerSchema);
module.exports = customer;
Controller
if(user.isProvider) {
var provider= new providermodel({
userId: user._id,
firstName: req.body.firstName,
lastName: req.body.lastName
});
provider.save(function(err, docs) {
if(!err) {
pprovidermodel.findOne({_id: provider._id}).populate('userId').exec(function(err, docs) {
if(err) {
console.log(err);
}
else {
console.log(docs); ----> **Here populate works, but no changes in the database**
console.log("SO " + docs.userId.email);
}
})
}
})
}else {
var customer = new customermodel({
userId: user.id,
firstName: req.body.firstName,
lastName: req.body.lastName
});
customer.save(function(err) {
if(!err) {
customermodel.findOne({}).populate('userId').exec(function(err, docs)
{
console.log(err);
console.log(docs);
})
}
})
}
I think it's right. Populate don't change values in database only retrieve values when code is running.

Handle schema references on mongoose

I'm trying to define a simple RESTful API using Node.js, mongoose and restify. The goal is to have users which can comment on profiles of others users. For this I have a comment endpoint that receives a text, the author and the target of the comment (other user).
I want to reference users so I defined next schemas:
User schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
"username": { type: String, unique: true, required: true },
"password": { type: String, required: true },
"comments": [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
});
mongoose.model('User', UserSchema);
Comment schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CommentSchema = new Schema({
date: { type: Date, default: Date.now },
text: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
target: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }
});
mongoose.model('Comment', CommentSchema);
I also have this controller (just showing createComment function):
exports.createComment = function(req, res, next) {
var authorId, targetId;
User.findOne({ _id: req.params.authorId}, function(err, author) {
if (author) {
User.findOne({ _id: req.params.targetId}, function(err, target) {
if (target) {
var comment = new Comment();
comment.text = req.params.text;
comment.author = author._id;
comment.target = target._id;
comment.save(function(err, comment) {
if (err) {
res.status(500);
res.json({
type: false,
data: 'Error occurred: ' + err
});
} else {
res.json({
type: true,
data: comment
});
}
});
} else {
res.json({
type: false,
data: 'User ' + req.params.authorId + ' not found'
});
}
});
} else {
res.json({
type: false,
data: 'User ' + req.params.targetId + ' not found'
});
}
});
};
So, I have three questions:
Why do I need to check if the user received exists? I would like to receive only the id and store it but I have to do two more queries to check it myself.
What I have to do to store in User only comments where that user is the target? solved in the edited code
How can I simplify this code? Is a pain to have async queries executed in order. I would like to have generic errors and not to have to handle each one.
EDIT: I've simplified the code using validations on the schema:
Comment schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = mongoose.model('User');
var CommentSchema = new Schema({
date: { type: Date, default: Date.now },
text: { type: String, required: true },
author: { type: Schema.Types.ObjectId, ref: "User", required: true },
target: { type: Schema.Types.ObjectId, ref: "User", required: true }
});
CommentSchema.path('author').validate(function(value, respond) {
User.findOne({ _id: value}, function(err, user) {
respond(!err && user);
});
}, 'Author doesn\'t exists');
CommentSchema.path('target').validate(function(value, respond) {
User.findOne({ _id: value}, function(err, user) {
respond(!err && user);
});
}, 'Target user doesn\'t exists');
mongoose.model('Comment', CommentSchema);
Controller:
exports.createComment = function(req, res, next) {
var comment = new Comment(req.body);
comment.save(function(err, comment) {
if (err) {
res.status(500);
res.json({
type: false,
data: 'Error occurred: ' + err
});
} else {
User.findOne({ _id: comment.target }, function(err, user) {
user.comments.push(comment);
user.save();
});
res.json({
type: true,
data: comment
});
}
});
};
The problem with this is that now I have to use _id on queries (I would like to use a custom id) and I'm doing three queries every time I want save a comment (2 for validation and one more to store the comment). Is there a better way to to this?
You can use the select option of query in mongo to select only the _id field of the user, like this:
User.findOne({_id:req.params.authorId}).select({_id:1}).exec(function(err,user) {})
After you pull the userTarget from mongo, you need to add the comment._id to his list of comments, and save him:
target.comments.push(comment._id);
target.save(function(err, targetAfterSaved) {})
read about async or q, they are my favorites Libraries to handle with async functions. For handle with errors like you want, you can add some listeners - here is the documentation from restify site.
Hope you understand, if you need any help let me know

Resources