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.
Related
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')
}
I want to create a DB with Users which also have a reference to another DB called "Library" which has "favourites" and "likes". I will show the idea here:
User Model
const userSchema = Schema({
username: {type: String, minlength: 4, maxlength: 10, required: true, unique: true},
email: {type: String, required: true, unique: true},
password: {type: String, required: true},
isVerified: { type: Boolean, default: false },
library: {type: Schema.Types.ObjectId, ref: 'Library'}
}, { timestamps: true});
Library Model
const librarySchema = new Schema({
likes: [{
likeId: {type: String},
mediaType: {type: String}
}],
favourites: [{
favId: {type: String},
mediaType: {type: String}
}],
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
Can you please tell me if this is the right way to implement these models or if there is a better way?
At the moment if I try to call
User.findOne({email: 'xxx#xxx.com'}).populate('library').exec(function (err, library)
it doesn't find anything...
Library POST request
router.post('/favourites', passport.authenticate('jwt', {session: false}), function (req, res) {
const favouritesFields = {};
if (req.body.favId) favouritesFields.favId = req.body.favId;
if (req.body.mediaType) favouritesFields.mediaType = req.body.mediaType;
Library.findOne({user: req.user._id}).then(library => {
if (library) {
Library.update({user: req.user._id}, {$push: {favourites: favouritesFields}})
.then(library => res.json(library));
} else {
new Library({user: req.user._id, favourites: favouritesFields}).save().then(library => res.json(library));
}
});
});
User POST request
router.post('/signup', function (req, res) {
const {errors, isValid} = validateSignupInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
// Check if email already exists
User.findOne({email: req.body.email}, function (user) {
if (user) {
return res.status(400).json({
title: 'Email already exists'
});
}
});
// Create and save the new user
let user = new User({
username: req.body.username.toLowerCase(),
email: req.body.email.toLowerCase(),
password: bcrypt.hashSync(req.body.password, 10)
});
user.save(function (err, result) {
if (err) {
return res.status(500).json({
title: 'An error occurred during the signup',
error: err
});
}
res.status(201).json({
title: 'User created',
obj: result
});
Your problem is not with the query you're making. there is no foundUser.library because one was never added.
You're adding users to libraries, but you're not adding libraries to your users. if you run the following code in your app:
Library.find({}).populate("user").exec(function(err, foundLibraries){
if (err){
console.log(err);
} else {
console.log(foundLibraries);
}
});
You would see that the libraries have their "user" properties, that when populated contain the entire user document as an object. But, the reason that isn't working for foundUser.library when you query for users is that foundUser.library was never assigned. you know how you're assigning the email, username and password when creating users, you have to do the same for the library property. Or, in your case, since a library is only created after the user, you can just set the value of user.library in the callback of creating/saving the library.
I am building a Mean Stack app with mongo as backend.
I have a church schema in my mongoose model which looks like this:
{
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var mongooseUniqueValidator = require('mongoose-unique-validator');
var sequenceGenerator = require('mongoose-sequence-plugin');
var unit = new Schema({
unit_id: {type: String},
unit_name: {type: String, unique: true},
unit_address: {type: String}
});
var schema = new Schema({
church_id: {type: String},
church_name: {type: String, required: true, unique: true},
email: {type: String, required: true, unique: true},
parishname: {type: String},
diocese: {type: String},
units: [unit],
church_reg_id: {type: String, required: true, unique: true},
address: {type: String},
city: {type: String},
zipcode: {type: Number},
state: {type: String},
church_contact_no: {type: Number}
});
schema.plugin(mongooseUniqueValidator);
schema.plugin(sequenceGenerator, {
field: 'church_id',
startAt: '0001',
prefix: 'CHURCH_',
maxSaveRetries: 3
});
module.exports = mongoose.model('Church', schema);
Now i am trying to update the units sub-document in my current schema which is already having data for other fields. Code for updating as is below:
router.patch('/addunit/:uname/:uaddress', function (req, res, next) {
console.log(req.params.uname);
Church.findOne({church_id: req.body.church_id}, function (err, church) {
if (err) {
return res.status(500).json({
title: 'No Church Details Found',
error: err
});
}
console.log(church.church_reg_id);
console.log(church.church_name);
church.units.push({
unit_name: req.params.uname,
unit_address: req.params.uaddress,
unit_id: req.body.church_id + "Unit_" + req.params.uname
});
console.log(church.units);
church.save(function (err, result) {
if (err) {
return res.status(500).json({
title: 'An error occured',
error: err
});
}
res.status(201).json({
message: 'Unit Added',
obj: result
})
});
});
});
Now the issue is the code runs successfully and the units field is updated successfully in the backend but I am getting below error after this.
D:\Church\node_modules\mongodb\lib\utils.js:123
process.nextTick(function() { throw err; });
^
TypeError: callback is not a function
at D:\Church\node_modules\mongoose\lib\model.js:267:5
at D:\Church\node_modules\mongoose\lib\model.js:195:9
at handleCallback (D:\Church\node_modules\mongodb\lib\utils.js:120:56)
at D:\Church\node_modules\mongodb\lib\collection.js:1062:5
at D:\Church\node_modules\mongodb-core\lib\connection\pool.js:469:18
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
Kindly please help in pinpointing the issue.
Was able to successfully update the document using findOneAndUpdate query and using $addToSet to add the sub-docs in the church schema.
Church.findOneAndUpdate({church_id: req.body.church_id}, update1, options, function (err, church) {
if (err) {
return res.status(500).json({
title: 'An error occured',
error: err
});
}
res.status(201).json({
message: 'Unit Added',
obj: church
})
});
Still not sure why model.save() query was throwing error.
I'm creating an app where you log workouts and I'm having some problems with Mongoose.
I have two schemas, one for workouts and one for exercises. When the user adds a new exercise, I want it to be stored inside the workout, and I've been trying this in a bunch of ways.
For now, the exercises are saved in a different collection in my MongoDB (don't know if this is the best way to do it), and I thought that it should save the exercise inside the workout.exercises, but there is only the objectID. How do I resolve this? Have looked at the populate function, but can't figure out how to get it to work.
addExercises
export function addExercise(req, res) {
if (!req.body.exercise.title) {
res.status(403).end();
}
const newExercise = new Exercise(req.body.exercise);
// Let's sanitize inputs
newExercise.title = sanitizeHtml(newExercise.title);
newExercise.cuid = cuid();
newExercise.sets = [];
newExercise.save((err, saved) => {
if (err) res.status(500).send(err);
});
Workout
.findOneAndUpdate(
{cuid: req.body.exercise.workoutCUID},
{$push: {exercises: newExercise}},
{upsert: true, new: true},
function (err, data) {
if (err) console.log(err);
});
}
getExercises
export function getExercises(req, res) {
Workout.findOne({cuid: req.params.cuid}).exec((err, workout) => {
if (err) {
res.status(500).send(err);
}
console.log(workout);
let exercises = workout.exercises;
res.json({exercises});
});
}
Workout
import mongoose from "mongoose";
const Schema = mongoose.Schema;
var Exercise = require('./exercise');
const workoutSchema = new Schema({
title: {type: 'String', required: true},
cuid: {type: 'String', required: true},
slug: {type: 'String', required: true},
userID: {type: 'String', required: true},
exercises: [{ type: Schema.Types.ObjectId, ref: 'Exercise' }],
date: {type: 'Date', default: Date.now, required: true},
});
export default mongoose.model('Workout', workoutSchema);
Exercise
import mongoose from "mongoose";
const Schema = mongoose.Schema;
var Workout = require('./workout');
const exerciseSchema = new Schema({
title: {type: 'String', required: true},
cuid: {type: 'String', required: true},
workoutCUID: {type: 'String', required: true},
sets: {type: 'Array', "default": [], required: true}
});
export default mongoose.model('Exercise', exerciseSchema);
Based on your Workout schema, you declare the type of the Exercises field to be [{ type: Schema.Types.ObjectId, ref: 'Exercise' }]. This means that this field should be an array of Mongoose ObjectId's.
It appears that you are attempting to add the whole exercise object to the workout's exercises field, rather than just the ObjectId. Try modifying it this way:
const newExercise = new Exercise(req.body.exercise);
// Let's sanitize inputs
newExercise.title = sanitizeHtml(newExercise.title);
newExercise.cuid = cuid();
newExercise.sets = [];
newExercise.save((err, saved) => {
if (err) res.status(500).send(err);
// Nest the Workout update in here to ensure that the new exercise saved correctly before proceeding
Workout
.findOneAndUpdate(
{cuid: req.body.exercise.workoutCUID},
// push just the _id, not the whole object
{$push: {exercises: newExercise._id}},
{upsert: true, new: true},
function (err, data) {
if (err) console.log(err);
});
});
Now that you correctly have the ObjectId saved in the exercises field, .populate should work when you query the workout:
Workout.findById(id).populate("exercises").exec((err, workout) => {
// handle error and do stuff with the workout
})
Workout.findById(req.params.id).populate("exercises").exec((err,workout) =>{
res.status(200).json(workout);
})
It should work this way
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.