I would like to create mongoose model dynamically with a prefix variable.
I have a First UserSchema
var UserSchema = new Schema({
username: String,
blogs : [{type: mongoose.Schema.Types.ObjectId, ref: 'Blog' }],
)};
module.exports = mongoose.model('User', UserSchema);
And a BlogShema
var BlogSchema = new Schema({
blogName: String,
posts : [{type: mongoose.Schema.Types.ObjectId, ref: prefix + 'Post' }]
)};
module.exports = mongoose.model('Blog', BlogSchema);
Then a PostSchema
var PostSchema = new Schema({
blog: { type: String, ref: 'Blog' },
title: String,
content: String
)};
module.exports = mongoose.model(prefix + 'Post', PostSchema);
Each Schema is on separate js files:
posts.server.model.js
blogs.server.model.js
users.server.model.js
I would like the prefix variable in mongoose.model(prefix + 'Post', PostSchema); to get the value of username String from UserSchema which passed form a signup form through
require('../../models/users.server.model.js');
require('../../models/blogs.server.model.js');
require('../../models/posts.server.model.js');
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
mongoose = require('mongoose'),
User = require('mongoose').model('User'),
Blog = require('mongoose').model('Blog'),
Post = require('mongoose').model(prefix + 'Post');
passport.use('local-signup', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function(req, username, password, done) {
process.nextTick(function() {
User.findOne({ username : username }, function(err, user) {
if (err)
return done(err);
if (user) {
return done(null, false, req.flash('signupMessage', 'Username is already taken.'));
} else {
var newUser = new User();
newUser.username = username;
newUser.password = password;
newUser.email = req.body.email;
newUser.save(function(err) {
if (err)
throw err;
var site = new Site({user : newUser.username, siteTitle: newUser.username});
site.save(function(err) { // create website
if (err) {
return next(err);
} else {
newUser.sites.push(site); // push site'id in sites field in user
newUser.save(); // save user after site'id has been push
for (var i=0; i<basePages.length; i++) { // create pages from base.json file
var page = new Page(basePages[i]);
page.save(function(err, page) { // create page
if (err) {
return next(err);
} else {
site.pages.push(page); // push page'id in pages field in site
site.save(); // save site after pages'id has been push
};
});
};
return done(null, newUser);
};
});
});
};
});
});
}));
For resume, If signup username is john during signup I would like to create a johnpost collection john posts.
Related
There is a array of user IDs in currentUser.follow. Each user has posts with referenceId of PostSchema . Now I want to populate each user's post and store it in an array[userArray]. But due to scope issue the array remains empty. Please show me how I can get all the users with their post in the Array[userArray]
app.js
app.get("/", isLoggedIn, function(req, res){
var currentUser =req.user;
var userArray=[];
for(let fol of currentUser.follow){
User.findById(fol).populate("posts").exec(function(err, user){
if(err){
console.log(err);
}else{
console.log(user); // a user with populated posts
userArray.push(user);
console.log(userArray); //stores user but posts is not populated
}
});
}
console.log(userArray); // empty array
});
User Schema
var mongoose =require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var UserSchema = new mongoose.Schema({
name: String,
email: String,
username: String,
password: String,
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Post"
}
],
follow: [String]
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Post Schema
var mongoose =require("mongoose");
var PostSchema = new mongoose.Schema({
text: String,
image: String,
author:{
id:{
type: mongoose.Schema.Types.ObjectId,
ref : "User"
},
username: String
},
createdAt: {type:Date, default:Date.now}
});
module.exports= mongoose.model("Post", PostSchema);
Because User.findById is asynchronous so the second console.log(userArray); will excute before the result pushed to userArray.
There is a better way to do this with $in operator and async/await:
app.get("/", isLoggedIn, async function(req, res){
try {
var currentUser = req.user;
var userArray = await User.find({_id: {$in: currentUser.follow}}).populate("posts");
console.log(userArray);
} catch(err) {
console.log(err);
}
});
I'm new to MongoDB/Mongoose and trying to figure out how to map relationships between Schema. I don't think issue is with .populate(). At creation of new user and clients, I do not see the relationship reflected in collection
User (login via local/social) has many Clients.
A set of Clients belong to 1 User
Is this the correct way to declare foreign key? {type: Schema.Types.ObjectId, ref: 'Client'}
Should both Schema have the each other's foreign key to relate to one another?
Is there any additional code required when creating User/Client in order for the data from foreign key to show up? I read something about populate()
User
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
local: {
id: String,
email: String,
password: String,
name: String,
mobile: String,
clients: {type: Schema.Types.ObjectId, ref: 'Client'}
},
google: {
id: String,
token: String,
email: String,
name: String,
clients: {type: Schema.Types.ObjectId, ref: 'Client'}
}
});
module.exports = mongoose.model('User', userSchema);
Client
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var clientSchema = new Schema({
id: String,
firstname: String,
lastname: String,
mobile: String,
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
module.exports = mongoose.model('Client', clientSchema);
Create User
app.post("/api/user", function (req, res) {
const user = req.body;
console.log(user);
User.findOne({ 'local.email': user.email },
function (err, result) {
if (err) {
console.log(err);
handleError(err, res);
return;
}
if (result) {
res.status(500).send("Email already exists in database");
} else {
var newUser = new User();
newUser.local.password = generateHash(user.password);
newUser.local.email = user.email;
newUser.local.name = user.name;
newUser.local.mobile = user.mobile;
newUser.save(function (err, result) {
res.status(201).send("User added to database");
});
}
});
});
Create Client
app.post("/api/client", function (req, res) {
const client = req.body;
console.log(client);
Client.findOne({
$and: [
{ firstname: client.firstname },
{ lastname: client.lastname }
]
},
function (err, result) {
if (err) {
console.log(err);
handleError(err, res);
return;
}
if (result) {
res.status(500).send({msg:"Client already exists in database"});
} else {
var newClient = new Client();
newClient.firstname = client.firstname;
newClient.lastname = client.lastname;
newClient.mobile = client.mobile;
newClient.save(function (err, result) {
res.status(201).send("Client added to database");
});
}
});
});
I am new to express/mongoose and trying to implement User-Post relationship.Aka 1:Many.
I have two models,
// *models/user.js
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
email: {type: 'string'},
password: {type: 'string'},
posts: [{type: mongoose.Schema.Types.ObjectId, ref: 'Post'}]
});
module.exports = mongoose.model('User', userSchema);
// *models/post.js
var mongoose = require('mongoose');
var postSchema = mongoose.Schema({
user : { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
body: String,
title: String
});
module.exports = mongoose.model('Post', postSchema);
server.js,
Post = require('./models/post');
User = require('./models/user');
app.post('/api/posts',(req,res)=>{
User.findOne({email: "valid_email"},(err, user)=>{
var post = {
title: "Post 1 Title",
body: "Post 1 body",
user: user._id
}
Post.create(post, (err, post)=>{
res.send(post);
});
});
});
Now, this approach is working for saving the Post which belongs to user with email "valid_email".I can reach out it's user by populating.
app.get('/post', (req,res)=>{
Post.findOne(title: "Post 1 title")
.populate('user')
.exec(function (err, user) {
if (err) throw err;
console.log(user._id, user.email);
res.end();
});
});
I thought generating a Post in the callback of finding a User would automatically push it to User's posts.Not only it is not how it works, i can't push manually to "user.posts".
It says can't "push" to undefined.When i console.log(user.posts) i get undefined.
How is it that i can generate a Post belongsTo a User, then i can populate the user just by it's id, but can't push a Post to User? It gives undefined "user.posts" ?
!!! I got it working.
app.post('/api/posts', (req,res)=>{
User.findOne({id: _some_user_id}, (err,user)=>{
var poster = user;
var post = {title: req.body.title, body: req.body.body, category: req.body.category, user: poster}
Post.create(post, (err,post)=>{
res.send(post);
poster.posts.push(post);
poster.save();
});
});
});
I keep getting the same error "User is not a function" when I call my API method.
Has anybody got an ideas why this might be.
Api method:
//need to export the api methods.
var User = require('../models/user');
var passport = require('passport');
module.exports.create = function(req, res) {
//TODO: error checking.
var user = new User();
console.log(req);
user.firstName = req.body.firstName;
user.secondName = req.body.secondName;
user.email = req.body.email;
user.setPassword(req.body.password);
user.save(function(err) {
res.status(200);
});
};
user model:
var mongoose = require('mongoose');
var crypto = require('crypto');
var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
hash: String,
salt: String
});
userSchema.methods.setPassword = function(password){
};
userSchema.methods.validPassword = function(password) {
};
mongoose.model('User', userSchema);
Let me know if I need to enter any more files.
Thanks
You need to export the mongoose model you created at the end of the User Model file. Something like
module.exports = mongoose.model('User', userSchema);
I have my parent schema defined like this:
User.js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var PasswordSchema = require('./Password');
var UserSchema = new Schema({
name: { type: String, required: true },
password: PasswordSchema
});
mongoose.model('User', UserSchema);
My children schema defined like this:
Password.js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var crypto = require('crypto');
var PasswordSchema = new Schema ({
_id: false,
hashedPassword: { type: String, required: true },
salt: { type: String, default: '' }
});
var passwordRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,24}$/;
PasswordSchema.virtual('password')
.set(function (password) {
if (passwordRegex.test(password))
{
this.invalidate('password', 'Invalid password format');
}
});
mongoose.model('Password', PasswordSchema);
module.exports = PasswordSchema;
Now I used these Models schema in my controller like this:
user.js:
require('../models/User');
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Password = mongoose.model('Password');
router.post('/register', function (req, res, next) {
var user = new User(req.body);
var password = new Password({ password: 'abcd1234' });
console.log(password.$__.validationError.errors['hashedPassword']); // Here it works I got the validation error
user.password = password;
user.password.$__.validationError = password.$__.validationError; // WORKAROUND
console.log(user.password.$__.validationError.errors['hashedPassword']); // Here it doesn't work no validation error anymore ...
user.save(function (err) {
if (err)
console.log(":(");
else
console.log(":)");
});
});
module.exports = router;
Question:
So my problem now is that no matter what password I send to my children virtual it doesn't invalidate the process. How could I invalidate the mongoose save action from a children virtual ? Is there an other better option ?
Question Updated:
In user.js why the variable password has the validation error and when I assign it to user.password I don't have the validation error anymore ? How can I correct it ?
** Update 2:**
I have found a workaround see user.js: I just assign the required property to generate validation error. But it looks really not clean is there another way?
Here is one good example https://gist.github.com/swaj/1350041, refactor it as below
PasswordSchema.virtual('password')
.get(function(){
return this._password;
})
.set(function (password) {
this._password = password;
// invoke crypto to hash and encrypt password, then assign it to hashedPassword
this.hashedPassword = password; // this is just for test
});
PasswordSchema.path('hashedPassword').validate(function(v) {
if (v) {
if (passwordRegex.test(v)) {
this.invalidate('password', 'Invalid password format');
}
}
if (!v) {
this.validate('password', 'password required');
}
}, null);
Test codes
var user = new User({name: 'dd'});
user.password = new Password({password: 'asdfASF123444'});
user.save(function (err) {
if (err)
console.log(err);
else
console.log("save user successfully");
});
Validation error is
{ [ValidationError: User validation failed]
message: 'User validation failed',
name: 'ValidationError',
errors:
{ password:
{ [ValidatorError: Invalid password format]
properties: [Object],
message: 'Invalid password format',
name: 'ValidatorError',
kind: 'user defined',
path: 'password',
value: undefined } } }
Per invalidate source code
Document.prototype.invalidate = function (path, err, val) {
if (!this.$__.validationError) {
this.$__.validationError = new ValidationError(this);
}
// ...
We know invalidate function belong to Document.
password.$__.validationError.errors['hashedPassword']
You define the validation for PasswordSchema, not in UserSchema. so user.password.$__.validationError.errors['hashedPassword'] is not valid.
Test your code with
var user = new User({name: 'dd'});
user.password = new Password({password: 'asdfwe32113'});
user.save(function (err) {
if (err)
console.log(err);
else
console.log("save user successfully");
});
Validation will be triggered, however, with this code
`user.password = new Password({hashedPassword: 'asdfwe32113'});`
This validation is NOT triggered.
Because for virtual field, only the correct virtual name field is updated then the .set function could be called.
Also please add those codes to virtual('password'), to make sure the hashedPassword could be set correctly.
if (passwordRegex.test(password)) {
this.invalidate('password', 'Invalid password format');
}else {
this.hashedPassword = password;
}
For the second question, require('../models/User'); must be invoked before mongoose.model() to make sure the User.js is parsed firstly, and the User could be added into mongoose.model in User.js. So in user.js could find this User model from mongoose. JavaScript is an interpreted programming language, so we should tell the JS engine the file parsed order in this way.