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')
}
Related
So I have a primary mongoDB object that has multiple documents nested within. I want to access a specific document in an array and modify one of its values. This is my document setup
const sectionSchema = new mongoose.Schema({
name: String,
items: [itemSchema],
currentAmount: {
type: mongoose.Decimal128,
default: 0
},
limitAmount: {
type: mongoose.Decimal128,
required: true
},
isActive: {
type: Boolean,
default: 0
}
});
const Section = new mongoose.model("Section", sectionSchema);
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique:true
},
email: {
type: String,
lowercase: true,
trim:true,
required: true,
unique: true
},
password: {
type: String,
required: true
},
sections: [sectionSchema]
});
const User = new mongoose.model("User", userSchema);
I've added some dummy values to fill the database, including the other testSection and testItems.
const testSection2 = new Section({
name: "Vacation",
items: [testItem3,testItem4],
currentAmount: 0,
limitAmount: 800,
isActive: 1
});
const testUser = new User({
username: "wavey123",
email: "wvy#123.com",
password: "wvy123",
sections: [testSection1,testSection2]
});
I've tried different iterations of the .findOneAndUpdate methods with no success like:
app.post("/sSelect", function(req,res){
const selectedSection = req.body.sectionName;
User.findOneAndUpdate({sections: {$elemMatch: {isActive: 1}}},{sections: {isActive: 0}},{new: true}, function(err, aSection){
if (err){
console.log(err)
}
console.log(aSection);
})
User.findOneAndUpdate(({sections: {$elemMatch: {name: selectedSection}}}),{$set: {sections: {isActive: 1}}},{new: true}, function(err, aSection){
if (aSection){
res.redirect("/");
}
})
I end up with my base document looking like this:
[
{
_id: ObjectId("629a971bb8a72843a07df0fd"),
username: 'wavey123',
email: 'wvy#123.com',
password: 'wvy123',
sections: [
{
currentAmount: Decimal128("0"),
isActive: false,
_id: ObjectId("629a9756792a3b21872c329f"),
items: []
}
],
__v: 0
}
]
This happens after the first .findOneAndUpdate. Cant seem to get my head around it.
so i just scrapped the whole .findOneAndUpdate and just used JS to find the isActive key and manipulate it like so:
app.post("/sSelect", function(req,res){
const selectedSection = req.body.sectionName;
User.findOne({}, function(err, aSection){
aSection.sections.forEach(function(section){
if(section.isActive === true){
section.isActive = false;
console.log(section.isActive)
aSection.save();
}
})
});
User.findOne({}, function(err, aSection){
aSection.sections.forEach(function(section){
if(section.name === selectedSection){
section.isActive = true;
console.log(section.name,section.isActive)
aSection.save();
}
})
});
res.redirect("/");
:)
So I have the following layout for my app:
UserSchema (contains username and password)
var UserSchema = new mongoose.Schema({
firstName: String,
lastName: String,
email: String,
username: String,
password: String
}, options);
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
StudentSchema (discriminator() on User) with additional fields
var StudentSchema = new mongoose.Schema({
indexNumber: String,
courses: [{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Course"
},
entryDate: {
type: Date,
default: Date.now
}
}]
}, options);
module.exports = User.discriminator("Student", StudentSchema);
TeacherSchema (discriminator() on User) with additional fields
var TeacherSchema = new mongoose.Schema({
degree: String,
courses: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Course"
}]
}, options);
module.exports = User.discriminator("Teacher", TeacherSchema);
I am using passport local-strategy to User.register().
router.post("/register", function(req,res) {
var newUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
username: req.body.username
});
User.register(newUser, req.body.password, function(err, user) {
if(err) throw err;
passport.authenticate("local")(req, res, function() {
req.flash("success", "Welcome " + user.username + ".");
res.redirect("/");
});
});
});
Now all this is under same collection users in db (because of discriminators)
How do I, after an user registers, "promote" him to student or teacher, so I can have those additional fields in his object?
Try setting __t to "Teacher" or "Student" using an update command on the User model. I think the default discriminator key is __t so you need something like:
user.update({$set: {__t: "Student" } }, callback)`
Then you'll need to pull it with the Discriminator again to get your fields:
const Student = User.discriminator("Student", StudentSchema);
Student.findById(id, callback)
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.
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.
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!'
});
});
})