How to use mongoose custom validator - javascript

I have a mongoose schema that I want to validate. What I want to do is to check for whenever the age is present, then birthYear cannot be empty. I have tried both the required and validate property but it isn't working :(
const mongoose = require('mongoose');
const person = new mongoose.Schema({
age: { type: Number },
birthYear: {
type: String,
required: function () {
return this.age&& !this.age.length;
},
validate: [validates, 'Cannot be empty']
},
fullName: { type: String }
})
function validates(value) {
return this.age && !value;
}

Related

How do I reference mongoose model to another model?

I have a mongoose schema for stories that looks like this:
{
id: {
type: Number,
default: 0
},
title: {
type: String,
maxLength: 60
},
author: {
userid: {
type: Number
},
username: {
type: String
}
}
chapters: [chapter],
numchapters: {
type: Number,
default: 1
},
favs: {
type: Number,
default: 0
},
completed: {
type: Boolean,
default: false
}
}
What I'm trying to do is reference a document in a separate collection (users), and use the values of its userid and username fields in the author field.
how do I do this?
current code:
storyobj.populate('author', {path: 'author', model: 'users', select: 'userid username'}, (err) => {
if (err) {
console.log(err)
}
})
just in case it's relevant, the structure of the users collection looks like this:
{
username: {
type: String,
},
email: {
type: String,
},
password: {
type: String,
},
userid: {
type: Number
},
isAdmin: {
type: Boolean,
default: false
},
banned: {
type: Boolean,
default: false
}
}
EDIT:
I've changed the author field in the Stories model to look like this:
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
This is so I tell Mongoose, "Hey, I want this field to reference a user in the User collection".
Here are some more details that I hope will be of help.
Full code:
var storydb = require('../models/stories/story');
var chapterdb = require('../models/stories/chapter');
var userdb = require('../models/user');
const file = JSON.parse(fs.readFileSync('test.json')); // this is a file with the data for the stories I am trying to insert into my database
for (const d in file) {
var storyobj = new storydb({
id: d,
chapters: []
});
for (let e = 0; e < file[d].length; e++) {
var abc = file[d][e];
var updatey = {
chaptertitle: abc.chapter,
chapterid: parseInt(abc.recid),
words: abc.wordcount,
notes: abc.notes,
genre: abc.gid.split(', '),
summary: abc.summary,
hidden: undefined,
loggedinOnly: undefined,
posted: new Date(Date.parse(abc.date)),
bands: abc.bandnames.split(', ')
};
var kbv = getKeyByValue(userlookup, abc.uid);
storyobj.title = abc.title;
storyobj.numchapters = file[d].length;
storyobj.favs = file[d][0].numfavs;
updatey.characters = abc.charid.split(/, |,/g);
storyobj.chapters.push(updatey)
}
storyobj.save();
}
In file, there's a unique ID representing the author of each story. kbv returns the userid associated with that unique ID (note that they're NOT the same).
Now, here's where I'm having trouble:
What I want to do is find a user matching the userid in kbv, and make that the author property in the story model.
The code I'm currently using to try and achieve that:
storydb.findOne({storyobj}, 'author').populate("author", (f) => console.log(f));
const Stories = require("./path/to/model");
Stories
.find({ /* query */ }, { /* projection */ })
.populate("author.username", ["userid", "username"])
.then(/* handle resolve */)
.catch(/* handle rejection */)
For this to work, you have to add a ref key to the userid key in your model, where the ref value is the name of the model it's referencing.
Story.model.js
const StorySchema = new Schema({
author: {
userid: { type: Schema.Types.ObjectId, ref: "users", required: true },
/* other props */
}
/* other props */
});

Dynamic query in mongo and NodeJs asking for fields of documents embedded?

I am trying to make a dynamic query based on multiple selection of the user.
In my application I have the Publication schema that has the Pet schema embedded as follows:
var status = ["public", "private", "deleted"];
var publication_schema = new Schema({
pet:{
type: Schema.Types.ObjectId,
ref: "Pet"
},
status: {
type: String,
enum: status,
default: status[0]
}
});
module.exports = mongoose.model('Publication', publication_schema);
var pet_schema = new Schema({
type: {
type: String,
require: true
},
createdDate: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Pet', pet_schema);
Insyde an async method I build the query, getting all the user input values from the object filter, also I have the query object where I push the different criteria and use it with an $and
let query = {};
let contentQuery = []
if (filter.public && !filter.private) {
contentQuery.push({ status: { $eq: "public" } });
} else if (filter.privada && !filter.public) {
contentQuery.push({ status: { $eq: "private" } });
}
query = { $and: contentQuery }
try {
const publication = await Publication.find(query).populate('pet');
} catch (e) {
console.log(e)
}
the problem is when I want to add more criteria such as follows:
if (filter.specie) { // for example filter.specie equals 'cat'
contentQuery.push({ pet: { type: { $eq: filter.specie } } });
}
I get the error:
'Cast to ObjectId failed for value "{ type: { \'$eq\': \'cat\' } }" at path "pet" for model "Publication"',
name: 'CastError',
stringValue: '"{ type: { \'$eq\': \'cat\' } }"',
kind: 'ObjectId',
value: { type: { '$eq': 'cat' } },
path: 'pet',
reason: undefined,
model: Model { Publication } }
So. How can I do to query the fields of publication and also the pet fields inside publication?
You can have a look on Populate Query Conditions
Instead of .populate('pet') you could do something like
Publication.find({})
.populate({
path: 'pet',
match: { specie: 'cat'},
// You can select the fields you want from pet, or remove the select attribute to select all
select: 'name -_id',
// Here you could add options (e.g. limit)
options: { limit: 5 }
}).exec();
The above query will get you all Publications with pet.specie equals to 'cat'

Can't remove the user id from upvotes section

I've tried almost everything splice, pop, shift, remove but I can't remove the user id which simple means downvote the post.
Here is my code:
// #type POST
// #route /api/question/upvote/:id
// #desc route for upvoting answers to questions
// #access PRIVATE
router.post('/upvote/:id', passport.authenticate('jwt', {session:false}), (req, res)=> {
Profile.findOne({user: req.user.id})
.then(profile => {
Question.findById(req.params.id)
.then(question => {
if(question.upvotes.filter(upvote => upvote.user.toString() === req.user.id.toString()).length > 0){
return res.status(400).json({noUpvote : 'User is downvoted the question'});
}
question.upvotes.unshift({user : req.user.id});
question.save()
.then(question => res.json(question))
.catch(err => console.log('Error on saving the upvote user id :' + err));
})
.catch(err => console.log('Error on getting the question : ' + err));
})
.catch(err => console.log('Error on finding the user : ' + err));
});
There are the three models in my application:
//Load the Person Model
const Person = require('../../models/Person');
//Load the Profile Model
const Profile = require('../../models/Profile');
//Load the Questions Model
const Question = require('../../models/Questions');
So the upvote is in question model.
The Person model contains the registration and login information.
The Profile model contains the Person details. The Question model contains question, answer, comment, and upvotes.
Here is the question model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const QuestionSchema = new Schema({
user : {
type: Schema.Types.ObjectId,
ref: 'myPerson'
},
textone: {
type: String,
required: true
},
texttwo: {
type: String,
required: true
},
name:{
type: String
},
upvotes: [{
user : {
type: Schema.Types.ObjectId,
ref: 'myPerson'
}
}],
answers:[{
user : {
type: Schema.Types.ObjectId,
ref: 'myPerson'
},
answer: {
type: String,
required: true
},
name:{
type: String
},
date: {
type: Date,
default: Date.now
}
}],
comments: [{
user : {
type: Schema.Types.ObjectId,
ref: 'myPerson'
},
name:{
type: String
},
text: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
}],
date: {
type:Date,
default: Date.now
}
});
module.exports = Questions = mongoose.model("myQuestion", QuestionSchema);
you can remove the id using filter.
question.upvotes = question.upvotes.filter(upvote => upvote.user.toString() !== req.user.id.toString());
question.save();
add it inside the if condition if this is where you want to remove it.

TypeError: Object #<Object> has no method 'findById' at exports.save

I have found several of these question on here, however the ones I have found deal mostly with HTML, I am having this problem on submitting of a login in java.
Very Simple Save Function
var model = require('../../models/User')
exports.save= function(req,res){
model.findById(req.body.id,function(err,doc){
if(!doc) doc = new model()
doc.email = req.body.email
doc.password = req.body.password
doc.save(function(err){
if(err){
req.flash('error',err)
}
else{
req.flash('success','User saved')
res.redirect('/')
}
})
})
}
my model is very long but here is a sample of it
//define schema
schema = new mongoose.Schema({
email: {
label: 'Email',
type: String,
lowercase: true,
unique: true,
required: true,
index: true,
validate: [
validate('len','6','100'),
validate('isEmail')
]
},
password: {
label: 'Password',
type: String,
required: true,
select: false,
get: function(){ return '********' },
set: function(v){
return bcrypt.hashSync(v,bcrypt.genSaltSync(12))
},
validate: [
validate('len','8','64')
]
},
model = mongoose.model('User',schema)
exports.name = 'user'
exports.description = 'User Model'
exports.schema = schema
exports.model = model
Submits to the function via modal and on every submit I get the err
TypeError: Object #<Object> has no method 'findById' at exports.save
Any help or ideas would be greatly appreciated
Change
var model = require('../../models/User')
to
var model = require('../../models/User').model
Also see the documentation on modules.

Node.js ORM2 check if field already exists

What is the best method to check if field value already exists.
This is my model:
// Set global
var moment = require('moment');
var _ = require('lodash');
// Create model
module.exports = function (orm, db) {
var Profile = db.define('profile',
// Field Properties
{
username: {type: 'text', required: true, unique: true},
name: {type: 'text', required: true},
email: {type: 'text', required: true},
password: {type: 'text', required: true},
birthday: {type: 'date', required: true},
gender: {type: 'enum', values: ["male", "female"], required: true},
join_date: {type: 'date'}
},
{
// Model hooks. Manual: https://github.com/dresende/node-orm2/wiki/Model-Hooks
hooks: {
beforeValidation: function() {
// Set join date to current date
this.join_date = new Date();
}
},
// Model Validations. Manual: https://github.com/dresende/node-orm2/wiki/Model-Validations
validations: {
username: [orm.enforce.security.username({length: 4}, 'Invalid username')],
email: [orm.enforce.patterns.email('Please enter a valid email')],
password: [orm.enforce.security.password('6', 'Invalid password')],
birthday: [orm.enforce.patterns.match(/\d{2}-\d{2}-\d{4}/, null, 'Invalid birthday')]
},
// Model methods. Extra functions and stuff
methods: {
}
});
};
And this is my register controller:
module.exports = function (req, res, next) {
// Get post params
var params = _.pick(req.body, 'formAction', 'username', 'password', 'email', 'confirm_password',
'birthday', 'gender', 'terms');
// If we try to register
if (params['formAction'] == 'register') {
// Manual validations
// Check if we agreed with the terms
if (params['terms'] != 1) {
res.send({error: 'You must agree to the terms of service'});
return false;
}
// Check if password was confirmed
if (params['password'] && params['password'] != params['confirm_password']) {
res.send({error: 'Please confirm your password'});
return false;
}
// Check if username already exists
// Try to register
req.models.profile.create({username: params['username'],
password: params['password'],
email: params['email'],
birthday: params['birthday'],
gender: params['gender'],
name: params['username']}, function (err, items) {
// Check to see if we have error
error = helpers.getError(err);
// Return error
if (error)
res.send({error: error});
});
}
// Show login form
else
res.sendfile(settings.path + '/public/register.html');
};
How can i check if username already exists in db? Now if i try to create i get DUP_KEY error from database.
Thanks,
Radu
Looks like adding a hook and using next() worked out
beforeCreate: function (next) {
obj = this;
Profile.exists({email: this.email}, function (err, exists) {
if (exists) {
return next(new Error("Email already exists"));
}
else
{
Profile.exists({username: obj.username}, function (err, exists) {
console.log(exists);
if (exists) {
return next(new Error("Username already exists"));
}
else
return next();
});
}
});
}

Categories