mongodb populate method not working - node.js

Here is my code for models
var postSchema = new mongoose.Schema({
created_by: {type: Schema.ObjectId, ref:'User', autopopulate: true }, //should be changed to ObjectId, ref "User"
created_at: {type: Date, default: Date.now},
text: String
});
var userSchema = new mongoose.Schema({
username: String,
password: String, //hash created from password
created_at: {type: Date, default: Date.now}
});
Below is the code of how i insert data and try to retrieve it using populate method.
Post.create({text: 'farheen123',created_by: '5587bb520462367a17f242d2'}, function(err, post){
if(err) console.log("Farheen has got error"+err);
else console.log(post);
});
//5587f5556e6f2b38244d02d1: _id of already created user
Post
.findOne({ _id: '5587f5556e6f2b38244d02d1' })
.populate('created_by')
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story);
// prints "The creator is Aaron"
});
The result that i get is below. It gives numm reference to created_by, instead of giving username and password of that id.
The creator is { _id: 5587f5556e6f2b38244d02d1,
text: 'farheen123',
created_by: null,
__v: 0,
created_at: Mon Jun 22 2015 17:15:25 GMT+0530 (IST) }

When you create an instance of the Post model, you need to assign the _id from the user as an ObjectId, not a string:
var ObjectId = require('mongoose').Types.ObjectId;
Post.create({
text: 'farheen123',
created_by: new ObjectId('5587bb520462367a17f242d2')
}, function(err, post) {
if(err) console.log("Farheen has got error"+err);
else console.log(post);
});

Related

Mongoose: Allow users one review per journal

I have three schemas:
const journalSchema = new mongoose.Schema({
title: String,
category: String,
subcategory: String,
review: [{type: mongoose.Schema.Types.ObjectId, ref: 'Review'}],
link: String,
description: String,
subscribers: Number,
posts: Number,
image: {
data: Buffer,
contentType: String},
date: Date
});
const userSchema = new mongoose.Schema ({
username: String,
nickname: String,
password: String,
journal: [{type: mongoose.Schema.Types.ObjectId, ref: 'Journal'}],
googleId: String,
age: {type: Date},
gender: {type: String, enum: ["male", "female"]},
admin: Boolean,
role: String
});
const reviewSchema = new mongoose.Schema({
author: {type: mongoose.Schema.Types.String, ref: 'User'},
content: String,
date: Date,
rating: {type: Number, min: 1.0, max: 5.0}
});
const Journal = mongoose.model("Journal", journalSchema);
const User = mongoose.model("User", userSchema);
const Review = mongoose.model("Review", reviewSchema);
Right now any user can leave any number of reviews on the same journal. I want to make it so that a user can leave only one review per journal.
Post route for getting reviews:
app.post("/stats/review", function(req, res){
if(req.isAuthenticated()){
const userNickname = req.user.nickname;
const userId = req.user.id;
const userReview = req.body.journalReview;
const userRating = req.body.rating;
const journalId = req.body.journalId;
Journal.findById({_id: journalId}, function(err, journal){
Review.find({_id: {$in: journal.review}}, function(err, foundReview){
foundReview.forEach(function(review){
if(review.author == userNickname){
console.log("Review from this user already exists");
}
else{
var date = new Date();
const review = new Review();
review.author = userNickname;
review.content = userReview;
review.rating = userRating;
review.date = date;
review.save()
.then((result) =>{
Journal.findOneAndUpdate(
{_id: journalId},
{$push: {
review: review
}},
{useFindAndModify: false},
function(err, success){
if(err){
console.log(err);
}
else{
res.redirect("back");
}
}
);
})
.catch((error) =>{
console.log(error);
})
}
});
})
})
}
else{
res.redirect("/login");
}
});
Is it possible to achieve this with the use of addToSet mongoose method? Couldn't find a fitting solution from similar problems.
I think you need to do some checks here
if the _id already exists in the review field just return some err msg

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.

Mongoose Subdocument array pushing

My scenario is if person1 accepting person2 deal means..the person1_id will save inside that person2 particular deal field accepted,i have tried the code it was working perfectly if a accepted user(person2) has one deal but in case of more than one deal it was updating but deleting other deals (i.e,the suppose the person2 having 3 deals means if person1 accepting 3rd deal the accepted user id was updating in 3rd deal and the 1st and 2nd deal was deleted).Anyone please help me how to save only the updated deal array
var incomingUser = req.user;//accepting user accesstoken in header(person1)
if(req.params.id){
var id = req.params.id;//deal id
console.log("DealId:"+id + "Acceptinguser:"+incomingUser.name);
User.findOne(
{
"deals": {
$elemMatch: {
_id: id
}
}
},
function(err, data){
console.log("Dealer:" +data.name);
console.log("deal:"+ data.deals);
if(err){
console.log("User not found");
res.send(new restify.ResourceNotFoundError('failed','Deal not found'));
return next();
}
var dealObj = _.filter(data.deals, { id: id })[0];
console.log("Deal Obj" + dealObj);
var acceptingUser = incomingUser;
console.log("accepting user:" +acceptingUser._id);
dealObj.accepted = acceptingUser._id;
console.log("accept id: "+ dealObj.accepted);
data.deals = dealObj;
console.log("data:"+ data.deals);
data.save(function (err, result){
console.log("Result:" + result);
if(err){
console.log("Internal error");
res.send(new restifyc.InternalError('failed','Error accepting'));
return next();
}
console.log("saved");
res.send(200,{user: result});
return next();
});
});
}
}
And my schema is
var dealSchema = new mongoose.Schema({
shopName: {type: String,required: true},
deal: {type: String,required: true},
price:{type: Number,required: true},
start:{type: Date,default: Date.now},
end:{type: Date},
expiry:{type: Date},
comments:{type: String},
accepted: {type:mongoose.Schema.Types.ObjectId, ref:'user'},//person1 _id
rejected: {type:mongoose.Schema.Types.ObjectId, ref: 'user'}
});
var userSchema = new mongoose.Schema({
name: { type: String,required: true},
phone: { type: Number, required: true,unique: true},
email:{type: String},
password: {type: String},
deals:[dealSchema]
}, {collection: 'user'});
mongoose.model('Deal', dealSchema);
mongoose.model('user', userSchema);
Yep in order to update specifically what you need you can use the <array>.$ for the specified position of the element:
User.update(
"deals": {
$elemMatch: {
_id: id
}
}, {
"$set": {
"deals.$" : {/*your deal data*/}
}
}, function(err, doc) {
});
More details on how to use the $ wildcard https://docs.mongodb.org/manual/reference/operator/update/positional/

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.

Cannot pull from mongoose array

In my schema I have two arrays with users, invitedUsers and joinedUsers. Invited array is created from request body
invitedUsers: req.body.invitedUsers
Joined array is updated when user clicks 'Join'
$addToSet: {joinedUsers: req.user}
I can then pull user from joinedUsers array by performing
{$pull: {joinedUsers: {_id: req.user._id}}}
However, I can't pull from invited users array, although user object is the same. Here is the complete code:
app.put('/decline/events/:id', function (req, res) {
var update = {$pull: {invitedUsers: {_id: req.user._id}}};
console.log(req.user._id);
Event.findByIdAndUpdate(req.params.id, update, function (err, event) {
if (err) {
res.send(err);
};
Event.find({})
.populate('owner')
.exec(function (err, events) {
if (err) {
res.send(err);
};
res.json(events)
});
});
});
Ajax call on client
$scope.declineInvitation = function (id) {
$http.put('/decline/events/' + id)
.success(function (data) {
})
.error(function (data) {
console.log('Error: ' + data);
});
};
Everything goes without errors, but invitedArray doesn't change, as if mongoose fails to find it. What might be a solution to this?
Update
Event Schema definition, as requested:
var eventSchema = new Schema({
title: {type: String},
description: {type: String, default: ''},
startDate: Date,
invitedUsers: {type: Array, default: []},
joinedUsers: {type: Array, default: []},
owner: {type: Schema.Types.ObjectId, ref: 'User'},
created_at: {type: Date, default: Date.now()},
updated_at: {type: Date, default: null},
location: String
}
The problem was because the _id in invitedUsers is a String and req.user._id is an ObjectId.
Calling toString() on _id will solve it:
var update = {$pull: {invitedUsers: {_id: req.user._id.toString()}}};

Resources