How to update inner document in node using mongoose - javascript

I want to insert cont and cont_type inside pro_picture. But this is not a embedded schema.
My schema:
var userSch = new Schema({
name: String,
email: String,
profile_picture: {
cont: String,
cont_type: String
}
});
var User = mongoose.model('user', userSch);
Now I want to find the user whose email is "something#example.com' and update the pro_picture.
I tried the following code.
User.findOne({email: data.email}, function (err, user) {
var userData = new User({
profile_picture: {
cont: data.pro_cont,
cont_type: data.pro_cont_type
}
});
user.profile_picture = userData;
console.log(user.profile_picture);
console.log(user);
user.save(function (err) {
if(err) throw err;
callback(rjTypes.result.SUCCESS);
});
It shows
profile_picture: null //in console
but the field is not present in collection

Finally i found it..
I solved by using this technique....
User.findOne({email: data.email}, function (err, user) {
user.profile_picture.cont = data.pro_cont;
user.profile_picture.cont_type = data.pro_cont_type;
console.log(user.profile_picture);
console.log(user);
//user.profile_picture.cont_type = data.pro_cont_type;
user.save(function (err) {
if(err) throw err;
callback(rjTypes.result.SUCCESS);
});
});

I think you're doing one nesting to much, try changing
var userData = new User({
profile_picture: {
cont: data.pro_cont,
cont_type: data.pro_cont_type
}
});
to
var userData = {
cont: data.pro_cont,
cont_type: data.pro_cont_type
};

Related

TypeError: User.findByIdAndUpdate is not a function

Answer
this fixed: mongoose.model('User').findByIdAndUpdate(... although it doesn't seem right. Should be another way to require this. Will figure out.
I have a signup form, where user can check a role to either create a candidate or an employer account. Let's say, user picked employer, then mongoose post hook will be triggered to create an employer inside employer collection and return employer._id so that I can save it to user collection as reference. In the last code snippet, findByIdAndUpdate is not triggered and failed silently. Log, outputs only a.
user.js (user model)
const mongoose = require('mongoose');
const { Schema } = mongoose;
const bcrypt = require('bcrypt-nodejs');
const {
updateUserWithEmployerId,
updateUserWithCandidateId
} = require('../api/user');
const { createCandidate } = require('../api/candidate');
const { createEmployer } = require('../api/employer');
const userSchema = new Schema({
firstName: String,
lastName: String,
email: {
type: String,
unique: true,
lowercase: true
},
password: String,
role: {
type: String,
enum: ['candidate', 'employer']
},
_employerId: {
type: Schema.Types.ObjectId,
ref: 'Employer'
},
_candidateId: {
type: Schema.Types.ObjectId,
ref: 'Candidate'
}
}, {
timestamps: true
});
userSchema.post('save', function(doc, next){
if(doc.role === 'employer'){
return createEmployer(doc._id)
.then(response => updateUserWithEmployerId(doc._id, response.employer._id))
// .catch(error => next(error))
// .catch(error => response.status(500).send(error))
}else if(doc.role === 'candidate'){
return createCandidate(doc._id)
.then(response => updateUserWithCandidateId(doc._id, response.candidate._id))
.catch(error => response.status(500).send(error))
}else{
throw new Error('User role not found!');
}
next();
});
const ModelClass = mongoose.model('User', userSchema);
module.exports = ModelClass;
employer.js(this is not model)
const Employer = require('../models/employer');
exports.createEmployer = (userId) => {
return new Promise(function(resolve, reject){
const employerInstance = new Employer({ _userId: userId });
if(employerInstance){
employerInstance.save((err, employer) => {
if(err){
reject({ message: "Error occured while creating employer!" });
}else{
resolve({ employer: employer });
}
});
}else{
reject({ message: "Error occured while creating employer!" });
}
});
};
user.js (not a model)
const User = require('../models/user');
exports.updateUserWithEmployerId = (userId, employerId) => {
return new Promise(function(resolve, reject){
console.log("a");
User.findByIdAndUpdate(userId, { _employerId: employerId }, { new: true },
(err, user) => {
console.log("user:", user);
if(err){
console.log("b");
reject({ message: "Error occured while updating user with employer id!" });
}else{
console.log("c");
resolve({ user: user });
}
});
console.log("d");
});
};
Server is working on http:4000
Mongodb connected with server: localhost
C:\Ajay Vemra\Mern Stack E-Commerce Website\backend\controllers\productController.js:41
product = await product.findByIdAndUpdate(req.params.id,req.body,{
^
TypeError: product.findByIdAndUpdate is not a function
at exports.updateProduct (C:\Ajay Vemra\Mern Stack E-Commerce Website\backend\controllers\productController.js:41:29)

Able to create multiple users with same email despite email set as unique in Mongoose

I am writing an API using Express, MongoDB, and Mongoose. I am somehow able to create multiple users with the same email. However, I shouldn't be able to create another user with the same email address. I have email unique: true in my user schema, but this isn't working as expected.
Here's my user schema:
var UserSchema = new Schema({
fullName: { type: String, required: [true, 'Full name is required.'] },
emailAddress: {
type: String, required: true, unique: true,
validate: {
validator: function (value) {
// check for correct email format
return /^[a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(value)
},
message: `Please enter a valid email address!`
}
},
password: { type: String, required: true }
});
My user authenticate method:
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ emailAddress: email })
.exec(function (err, user) {
if (err) {
return callback(err);
} else if (!user) {
var error = new Error('User not found');
error.status = 401;
return callback(error);
}
bcrypt.compare(password, user.password, function (error, user) {
if (user) {
return callback(null, user);
} else {
return callback();
}
});
});
}
My pre-save hook to hash the password:
UserSchema.pre('save', function (next) {
var user = this;
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
And finally, my user create route:
router.post('/', function (req, res, next) {
if (req.body.fullName &&
req.body.emailAddress &&
req.body.password &&
req.body.confirmPassword) {
if (req.body.password != req.body.confirmPassword) {
var err = new Error('Passwords do not match!');
err.status = 400;
return next(err);
}
// object with form input
var userData = {
fullName: req.body.fullName,
emailAddress: req.body.emailAddress,
password: req.body.password
};
// schema's 'create' method to insert document into Mongo
User.create(userData, function (error, user) {
if (error) {
var err = new Error('Please enter a valid email.');
err.status = 400;
return next(err);
} else {
// set location header to '/', return no content
res.status(201);
res.location('/');
req.session.userId = user._id;
return res.json(user);
}
});
} else {
var err = new Error('All fields required.');
err.status = 400;
return next(err);
}
});
MongoDB does not create unique index to a field if the field value has duplicates already stored in it.
In your case emailAddress must have duplicates already stored in the database.
You can check it by runing the code
mongoose
.model(#modelName)
.collection.createIndex( { "inviteCode": 1 });
After running this code you should be able to see a error message in the console.
Or You can check by running the below code if you have duplicate. The below will fetch documents if have duplicates:
mongoose
.model(#modelName).aggregate([{
"$group": {
"_id": "$loginStatus",
count: { $sum: 1 },
ids: { $push: "$_id" }
}
},{ $match: {
count: { $gt : 1 }
}}]);
If you emailAddress which are already duplicates you cant create unique: true on it. You would have to run the second query and find out the duplicate email address. You can find the documents with duplicate email in the ids array.

Cannot read property "rid" of undefined

[TypeError: Cannot read property 'rid' of undefined]
Is the error that I get when I try to execute this controller on my post route.
I've tested it out with Postman.
I've tried to console.log(result) but I get undefined.
My query gets executed and my row is inserted into my table. I've checked it. Password is also hashed.
The problem is that I don't get any out binds that should be returned.
Problematic code (IMO) is
...
.then(function(result) {
console.log(result);
cb(null, {
id: result.outBinds.rid[0],
email: result.outBinds.remail[0],
role: result.outBinds.rrole[0]
});
})
...
oracle-NodeDB Wrapper
var oracledb = require('oracledb');
module.exports.OBJECT = oracledb.OBJECT;
function executeSQL(config ,sql, bindParams , options) {
return new Promise(function(resolve, reject) {
oracledb.getConnection(
config,
function(err, connection) {
if (err) {
return reject(err);
}
connection.execute(
sql,
bindParams,
options,
function(err, result) {
if (err) {
doRelease(connection);
return reject(err);
}
resolve(result);
doRelease(connection);
});
});
});
}
function doRelease(connection) {
connection.release(
function(err) {
if (err) {
console.log(err.message);
}
}
);
}
module.exports.executeSQL = executeSQL;
Controller
var database = require('../database/oracledbWrapper');
var dbconfig = require('../database/dbconfig').dbconfig;
var jwt = require('jsonwebtoken');
var bcrypt = require('bcrypt');
exports.createUser = function(req, res, next) {
var user = {
email: req.body.email
};
var unhashedPassword = req.body.password;
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(unhashedPassword, salt, function(err, hash) {
if (err) {
return next(err);
}
user.hashedPassword = hash;
insertUser(user, function(err, user) {
var payload;
if (err) {
return next(err);
}
payload = {
sub: user.email,
role: user.role
};
res.status(200).json({
user: user,
token: jwt.sign(payload, config.jwtSecretKey, {expiresInMinutes: 60})
});
});
});
});
}
function insertUser(user, cb) {
var bindParams = {
email: user.email.toLowerCase(),
password: user.hashedPassword,
rid: {
type: database.NUMBER,
dir: database.BIND_OUT
},
remail: {
type: database.STRING,
dir: database.BIND_OUT
},
rrole: {
type: database.STRING,
dir: database.BIND_OUT
}
};
database.executeSQL(
dbconfig,
'insert into express_users (email, password, role ) values ( :email, :password, \'BASE\' ) returning id, email, role into :rid , :remail, :rrole',
bindParams,
{}
)
.then(function(result) {
console.log(result);
cb(null, {
id: result.outBinds.rid[0],
email: result.outBinds.remail[0],
role: result.outBinds.rrole[0]
});
})
.catch(function(err) {
console.log(err);
next(err);
});
}
Route
var RESTfulAPICon = require('../controllers/RESTfulAPI');
var indexCon = require('../controllers/index');
var views = require('express').Router();
views.route('/users').post(RESTfulAPICon.createUser);
exports.views = views;
The problem was in my wrapper , mainly here
module.exports.OBJECT = oracledb.OBJECT;
I export only the OBJECT property , but I try to access BIND_OUT properties later on. And they are non existent.
If I do the full export like this
module.exports.OBJECT = oracledb;
Then I can access BIND_OUT properties.

NodeJS put array in MongoDB doc.validate is not a function

I am trying to push array of objects into MongoDB through NodeJS.
So my schema
var UserSchema = new mongoose.Schema({
id: mongoose.Schema.ObjectId,
added: {
type: Date,
default: Date.now()
},
displayName: String,
login: String,
email: String,
phone: String,
password: String,
salt: String,
role: Number,
hasPremium: Boolean,
avatar: Buffer,
description: String,
additional: [{
name: String,
data: String
}],
cart: [{
exposition: Object,
offer: Object,
state: Number,
history: [{
date: Date,
state: Number,
modifier: Number
}]
}],
balance: Number,
topUps: [{
orderNumber: String,
sum: Number,
added: Date,
paid: Date
}],
lock: Boolean
});
My save controller
module.exports = function (passport) {
passport.use('signup', new LocalStrategy({
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, username, password, done) {
findOrCreateUser = function () {
// find a user in Mongo with provided username
UserModel.findOne({'login': username}, function (err, user) {
// In case of any error, return using the done method
if (err) {
console.log('Error in SignUp: ' + err);
return done(err);
}
// already exists
if (user) {
console.log('User already exists with username: ' + username);
return done(null, false, req.flash('message', 'User Already Exists'));
} else {
// if there is no user with that email
// create the user
var newUser = new UserModel();
// set the user's local credentials
newUser.displayName = req.param('displayName');
newUser.login = username;
newUser.password = createHash(password);
newUser.email = req.param('email');
newUser.phone = req.param('phone');
newUser.role = req.param('role');
newUser.description = req.param('description');
if (req.param('avatar')) {
var avatar = new Buffer(req.param('avatar')).toString('base64');
newUser.avatar = new Buffer(avatar, 'base64');
}
var adds = req.param('additional');
console.log(adds);
if (adds) {
newUser.additional = [];
for (var i = 0; i < adds.length; i++) {
newUser.additional[i] = {};
newUser.additional[i].name = adds[i].name;
newUser.additional[i].data = adds[i].data;
}
}
console.log(newUser.additional);
// save the user
newUser.save(function (err) {
if (err) {
console.log('Error in Saving user: ' + err);
throw err;
}
console.log('User Registration succesful');
return done(null, newUser);
});
}
});
};
// Delay the execution of findOrCreateUser and execute the method
// in the next tick of the event loop
process.nextTick(findOrCreateUser);
})
);
// Generates hash using bCrypt
var createHash = function (password) {
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
}
So, when I run this code I got strange error
TypeError: doc.validate is not a function
Even if I had no validation in my scheme.
What am I doing wrong?
Thank you
OMG, I don't know what happened but when I did it like
newUser.additional = req.param('additional');
Insted of this part of code
if (adds) {
newUser.additional = [];
for (var i = 0; i < adds.length; i++) {
newUser.additional[i] = {};
newUser.additional[i].name = adds[i].name;
newUser.additional[i].data = adds[i].data;
}
}
It worked perfectly. There is still magic for me...

Function is undefined, Bookshelf.js model function is not being recognized as a function

I am using Bookshelf.js to handle a user registration API end-point, designed with NodeJS and ExpressJS. But upon POSTing to the register url, I keep hitting an error at one of the User model functions.
Here is routes/index.js
var User = require(./models/User);
router.post('/register', function(req, res, next){
if(!req.body.username || !req.body.password){
return res.status(400).json({message: 'Please fill out all fields'});
}
try {
var hash = User.createPassword(req.body.password);
console.log(hash);
new User({email: req.body.username, name: req.body.username, password: hash}).save().then(function(model) {
return res.json({token: this.generateJWT()});
});
} catch (ex) {console.log(ex.stack);}
});
Here is models/Users.js
var jwt = require('jsonwebtoken');
var bcrypt = require('bcrypt');
var bookshelf = require('../config/bookshelf');
var User = bookshelf.Model.extend({
tableName: 'users',
constructor: function() {
bookshelf.Model.apply(this, arguments);
this.on('saving', function(model, attrs, options) {
console.log(this.createPassword(model.attributes.password));
});
},
createPassword: function(password) {
bcrypt.genSalt(10, function (err, salt) {
if(err) return next(err);
bcrypt.hash(password, salt, function (err, hash) {
if(err) return next(err);
return hash;
});
});
},
validPassword: function(password, encryptedPass) {
bcrypt.compare(password, user.encryptedPass, function (err, match) {
if(err) cb(err);
return (match) ? true : false;
});
},
generateJWT: function() {
// set expiration to 60 days
var today = new Date();
var exp = new Date(today);
exp.setDate(today.getDate() + 60);
return jwt.sign({
_id: this._id,
email: this.email,
exp: parseInt(exp.getTime() / 1000),
}, 'SECRET');
}
});
module.exports = User;
When I try to POST to register, I get the following stack trace:
TypeError: undefined is not a function
at \routes\index.js:185:21
at Layer.handle [as handle_request] (\node_modules\express\lib\router\layer.js:95:5)
at next (\node_modules\express\lib\router\route.js:131:13)
at Route.dispatch (\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (\node_modules\express\lib\router\layer.js:95:5)
...
In the stack trace, \routes\index.js:185:21 is the following line: var hash = User.createPassword(req.body.password); at createPassword.
So what am I doing wrong here? Why is it failing to recognize the createPassword function?
createPassword() is not defined as a static method (a.k.a. classProperties), but you are calling it as such. Try this model definition. It should expose createPassword() directly on the User class as a static method.
var User = bookshelf.Model.extend({ //instance methods
tableName: 'users',
constructor: function() {
bookshelf.Model.apply(this, arguments);
// ...
},
validPassword: function(password, encryptedPass) {
// ...
},
generateJWT: function() {
// ...
}
}, { //static methods
createPassword: function(password) {
// ...
}
});
Extra: You'll need to fix your createPassword, as it's async. Below I've converted it to a Promise-returning function (as bookshelf uses promises extensively) and show an example usage for your route handler
createPassword: function () {
return new Promise(function (resolve, reject) {
bcrypt.genSalt(10, function (err, salt) {
if (err) return reject(err);
bcrypt.hash(password, salt, function (err, hash) {
if (err) return reject(err);
resolve(hash);
});
});
});
}
// in route handler
if (!req.body.username || !req.body.password) {
return res.status(400).json({
message: 'Please fill out all fields'
});
}
try {
User.createPassword(req.body.password)
.then(function(hash) {
console.log(hash);
return new User({
email: req.body.username,
name: req.body.username,
password: hash
}).save();
}).then(function (model) {
return res.json({
token: this.generateJWT()
});
}).catch(function(ex) {
console.log(ex.stack);
});
} catch (ex) {
console.log(ex.stack);
}
User is a constructor but the method is defined on its prototype. You have to create an instance to call a method on. Try
var user = new User({email: req.body.username, name: req.body.username, password: hash});
var pwd = user.createPassword();
user.save()
Note that from the look of your method, it's probably better as a static method (it doesn't access this), which the other answer describes how to create.

Categories