mongoose user defined methods - node.js

I'm having trouble trying to add instance methods to my schemas.
Here is an example:
var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
var schema = new mongoose.Schema ({
first_name: {type: String, required: true, trim: true},
last_name: {type: String, required: true, trim: true},
email: {type: String, required: true, unique: true, dropDups: true, trim:true},
hash: {type: String, required: true}
});
schema.methods = {
encrypt: function(pwd) {
if (!pwd) return '';
else return bcrypt.hashSync(pwd, bcrypt.genSaltSync(10));
},
test: function(logentry) {
console.log(this.email + ': ' + logentry);
}
};
mongoose.model('Users', schema);
And then in my code elsewhere I try to call one of the methods:
var mongoose = require('mongoose');
var Users = mongoose.model('Users');
function testFunction(email) {
Users.find({email:email}, function(error, user) {
user.test('Trying to make mongoose instance methods work.');
});
}
testFunction('goofy#goober.com');
And then I get the following error (stacktrace omitted):
user.test('Trying to make mongoose instance methods work.');
^
TypeError: undefined is not a function
I cannot for the life of me figure this out..
I am using mongoose 3.8. I know I'm doing something wrong, but I need another, much smarter and experienced pair of eyes to help me find it.
I've tried defining the methods like this too:
schema.methods.encrypt = function(pwd) {...};
schema.methods.test = function(logentry) {...};
But it doesn't seem to matter.
There was only one previous post like this that I could find on stack overflow and they resolved their error by making sure that their methods were defined before they called mongoose.model('name', schema). I've got them defined before, so I don't think it's the same problem. Any help would be much appreciated.

The problem is that Users.find gives you an array.
So, either:
Users.find({ email: email }, function (e, users) {
users[0].test('foo bar whatever');
});
or:
Users.findOne({ email: email }, function (e, user) {
user.test('foo bar whatever');
});

Related

Mongoose findByIdAndUpdate

I trying to edit and update a form using mongoose. The code seems fine to me, but it doesn't work. I have tried so many ways but the updated version is still the same, I uses a put route to send the form, when I output req.body.studentInfo to the console, it is correct, but the update remains the same. Please help
This is my schema
var mongoose = require("mongoose");
var uniqueValidator = require('mongoose-unique-validator');
var passportLocalMongoose = require("passport-local-mongoose");
var mongoose = require("mongoose");
var UserSchema = new mongoose.Schema({
studentInfo: {
first_name: String,
middle_name: String,
last_name: String,
street: String,
town: String,
city: String,
region: String,
country: String,
studentId: String,
day: Number,
month: String,
year: Number,
},
username: {type: String, required:true, unique:true},
passport: String
});
UserSchema.plugin(uniqueValidator);
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("StudentInfo", UserSchema);
This is my App.js
app.put('/:id', function(req,res){
StudentInfo.findByIdAndUpdate(req.params.id, {$set: req.body.studentInfo}, function(err, updated){
console.log(req.params.id);
console.log(req.body.studentInfo);
if(err) {
console.log(err);
}
else {
res.redirect('/' + req.params.id);
}
});
});
The studentInfo is an object that contains the names of each variables in my form which I name was studentInfo[name of variable]. Please help
It should be specified that mongoose should return the updated document - by default it returns the original (this is also the behavior of mongodb). I think that if the code gets changed to this:
StudentInfo.findByIdAndUpdate(req.params.id, {$set: req.body.studentInfo}, { new: true }, function(err, updated){
...
});
you will receive the updated document in the callback.
As #Denny mentioned in his answer, mongoose will not return the updated document in the callback until you pass {new : true } option.
For Details and available options check findByIdAndUpdate Docs

Retrieve Array in Subdocument MongoDB

I have a Users model structure somewhat like this:
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
todosDo: [models.Do.schema],
}
And the child "Do" schema somewhat like this (in a different file):
const doSchema = new mongoose.Schema({
name: {type: String, default : ''},
user: {type: mongoose.Schema.ObjectId, ref: 'User'},
createdAt: {type : Date, default : Date.now}
});
And I'm trying to figure out how to retrieve the todosDo array for the signed in user. This is what I've got so far:
// Get all "Do" todos from DB
// Experimenting to find todos from certain user
User.findById(req.user.id, function(err, user){
if(err){
console.log(err);
} else {
doTodos = user.todosDo, // this obviously doesn't work, just an idea of what I was going for
console.log(doTodos);
finished();
}
});
Am I referencing the child/parent wrong or am I just not retrieving the array right? Any help is greatly appreciated!
As far I guess you may want to edit as raw js objects so you need to use lean() function. without using lean() function user is mongoose object so you can't modify it.
can try this one:
User.findById(req.user.id)
.lean()
.exec(function (err, user) {
if(err){
console.log(err);
return res.status(400).send({msg:'Error occurred'});
}
if(!user) {
return res.status(400).send({msg:'User Not found'});
}
doTodos = user.todosDo;
console.log(user.todosDo); // check original todos
console.log(doTodos);
return res.status(200).send({doTodos : doTodos }); // return doTodos
});
and to refer child schema in parent schema from different model you can access a Model's schema via its schema property.
say in doSchema.js file
const doSchema = new mongoose.Schema({
name: {type: String, default : ''},
user: {type: mongoose.Schema.ObjectId, ref: 'User'},
createdAt: {type : Date, default : Date.now}
});
module.exports = mongoose.model( 'DoSchema', doSchema );
in user.js file
var DoModel = require('./doSchema');// exact path
const userSchema = new mongoose.Schema({
email: { type: String, unique: true },
password: String,
todosDo: [DoModel.schema],
}
Thanks for your help everybody! My problem was that I needed to push all the newly created todos in the post route to todosDo, so then I could retrieve them at the get route. Everything's working now!

Where to place email validator within project?

I am on day 2 of learning node and Java Script. I have been following basic tutorials and decided to attempt and implement simple email validation into my code.
The problem is i am not sure where to place the code - i have a server.js file that holds all of the CRUD operations and a Mongoose model that which ensures the correct data is entered. Does anyone have any advice as to the best way to validate a user-entered email using this module?
//Email-validation npm module
var validator = require("email-validator");
validator.validate("test#email.com");
//Mongoose model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Tickets = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
address: {
type: String,
required: true
},
price: {
type: Number,
required: true,
min: 1,
max: 100
}
});
module.exports = mongoose.model('Ticket', TicketSchema);
Validate email before saving object. Code should look something like this:
Tickets.pre('save', function (next) {
var ticket = this;
if (!ticket.isModified('email')) {
next();
} else {
var valid = validator.validate(ticket.email);
if(valid) {
next();
} else {
next(valid);
}
}
});

Can't get mongoose.js to expire old documents at the right time

According to http://mongoosejs.com/docs/api.html#schema_date_SchemaDate-expires I can expire old documents by adding an 'expires' attribute to a mongoose schema. The documents do get removed, when I use the code below, but after around 4 minutes instead of 10 (I've also tried with much higher numbers and e.g. the number 600 instead of '10m').
Any help is appreciated.
Versions:
mongoose: 3.8.12
MongoDB: 2.4.9
// models.js --------------------------------------------------------
var mongoose = require('mongoose');
var signupSchema = mongoose.Schema({
email: {type: String, required: true, unique: true},
token: {type: String, required: true, unique: true},
createdAt: {type: Date, expires: '10m'}
});
signupSchema.pre('save', function(next) {
this.createdAt = Date.now();
next();
});
mongoose.model('Signup', signupSchema);
// user.js (the code below is in a route) ---------------------------
var mongoose = require('mongoose');
var SignupModel = mongoose.model('Signup');
var newSignup = new SignupModel({
email: 'a#a.a',
token: 'token'
});
newSignup.save(function(err, signup) {
// do stuff
});

locomotivejs and mongoose: passing promise variables to a controller

preamble:
I'm not sure if this is the best way to ask this question as I'm fairly sure it's more general than I'm making it and there's probably a global pattern that address my concern.
For the moment here's the question in the context of the original code that I'm working with.
Given a locomotivejs controller, let's call it Contact_Controller, with general structure like this:
'\controllers\contact_controller.js
var locomotive = require('locomotive')
, Controller = locomotive.Controller
, Contact = require('../models/contact')
, sender = require('../helpers/sender')
;
var Contact_Controller = new Controller();
and then:
Contact_Controller.list = function() {
//Provides a list of all current (successful) contact attempts
var controller = this;
Contact.find({status: 1}, function(err, results) {
if(err) {
controller.redirect(controller.urlFor({controller: "dashboard", action: "error"}));
}
controller.contacts = results;
controller.render();
});
};
and the model:
'/models/contact.js
var mongoose = require('mongoose')
, mongooseTypes = require('mongoose-types')
, pass = require('pwd')
, crypto = require('crypto')
, Schema = mongoose.Schema
, Email = mongoose.SchemaTypes.Email;
var ContactSchema = new Schema({
email: {type: Email, required: true},
subject: {type: String, required: true },
message: { type: String, required: true},
status: {type: Number, required: true, default: 1},
contact_time: {type: Date, default: Date.now}
});
module.exports = mongoose.model('contact', ContactSchema);
Inside the list action of the contact_controller I'd really rather not use controller = this; I generally prefer using redirect = this.redirect.bind(this); style localized binding to handle these sort of situations.
However, I can't think of a way to return the results to the controller's this object without creating a global variable version of this and having the promise's callback talk to that. Is there a better way to return the results variable or expose the contact_controller object in this context?
Do you mean this?
Contact.find({status: 1}, function(err, results) {
if (err) {
this.redirect(this.urlFor({this: "dashboard", action: "error"}));
return; // you should return here, otherwise `render` will still be called!
}
this.contacts = results;
this.render();
}.bind(this));
^^^^^^^^^^^ here you bind the controller object to the callback function

Resources