Here's my code:
var userSchema = new mongoose.Schema({
email: String,
password: String,
role: Something
});
My goal is to define the role property to have specific values ('admin', 'member', 'guest' and so on..), what's the better way to achieve this? Thanks in advance!
You can do enum.
var userSchema = new mongoose.Schema({
// ...
, role: { type: String, enum: ['admin', 'guest'] }
}
var user = new User({
// ...
, role: 'admin'
});
There isn't really a way that I know of to have specific values possible for role, but maybe you'd like to create multiple object types based off of a master object type, each with their own roles (and anything else you want to distinguish). For example...
var userSchema = function userSchema() {};
userSchema.prototype = {
email: String,
password: String,
role: undefined
}
var member = function member() {};
member.prototype = new userSchema();
member.prototype.role = 'member';
var notSupposedToBeUsed = new userSchema();
var billTheMember = new member();
console.log(notSupposedToBeUsed.role); // undefined
console.log(billTheMember.role); // member
Another possibility is have userSchema with a constructor that easily allows you to select one of the built in values. An example...
var userSchema = function userSchema(role) {
this.role = this.role[role];
// Gets the value in userSchema.role based off of the parameter
};
userSchema.prototype = {
email: String,
password: String,
role: { admin: 'admin', member: 'member', guest: 'guest' }
}
var a = new userSchema('admin');
var b = new userSchema('blah');
console.log(a.role); // 'admin'
console.log(b.role); // undefined
More: http://pivotallabs.com/users/pjaros/blog/articles/1368-javascript-constructors-prototypes-and-the-new-keyword
Related
I'm trying to return a result from my mongoose find operation. I know a lot of question have already been asked for this but i think this is different. Here's my user :
var UserSchema = new mongoose.Schema({
variable: {type: mongoose.Schema.ObjectId, ref: 'Variable'}
});
My user have a method to retrieve his variable. Here's the problem.
UserSchema.methods.getVariable = function() {
//TODO ?
}
I don't know how to populate my field and then return the result of the populate...
I think you can just use populate
var UserSchema = new mongoose.Schema({
variable: {type: mongoose.Schema.ObjectId, ref: 'Variable'}
});
UserSchema.
findOne({your:"query"}).
populate('variable').
exec().
then(
user=>console.log("user is:",user)
);
When do models receive their prototype?
I know that embedding is generally the answer here, but I have a special case.
If I call to another model in an instance's custom method, it seems to fail.
The error I'm getting:
Fish.find is not a function at model.UserSchema.methods.fishes
The Fish model is made into a model:
// Require mongoose to create a model.
var mongoose = require('mongoose'),
User = require('./user.js');
// Create a schema of your model
var fishSchema = new mongoose.Schema({
name: String,
category: String,
user: { type: mongoose.Schema.Types.ObjectId, ref:'User' }
});
// Create the model using your schema.
var Fish = mongoose.model('Fish', fishSchema);
// Export the model of the Fish.
module.exports = Fish;
The User model calls to the Fish model within the fishes custom instance method:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt-nodejs'),
Fish = require('./fish');
//||||||||||||||||||||||||||--
// CREATE USER SCHEMA
//||||||||||||||||||||||||||--
var UserSchema = new Schema({
name: { type: String, required: true },
phoneNumber: {
type: String,
required: true,
index: { unique: true },
minlength: 7,
maxlength: 10
},
password: { type: String, required: true, select: false }
});
// … some bcrypt stuff…
// Access user's fishes - THIS IS WHAT'S MESSING UP!!
UserSchema.methods.fishes = function(callback) {
Fish.find({user: this._id}, function(err, fishes) {
callback(err, fishes);
});
};
module.exports = mongoose.model('User', UserSchema);
When I call .fishes() in my seeds, it claims that Fish.find is not a function.
Why!? Any help would be greatly appreciated!
The issue is a circular import (fish.js requires user.js that requires fish.js, etc).
You can work around that by resolving the model class at runtime:
UserSchema.methods.fishes = function(callback) {
mongoose.model('Fish').find({user: this._id}, function(err, fishes) {
callback(err, fishes);
});
};
I am having problem managing the states of different types of users using Passport.js in Express.js 4.x.
I have 3 kinds user collections in my mongodb database
1. Member (has his own profile page)
2. Operator (has his own dashboard)
3. Admin (handles the backend)
I have created their separate Login/Registration systems. But only member seems to work, and the others don't. I have even written different sets of login/registration strategies for each user.
Like for the member passport.use('signup') and passport.use('login').
for operator passport.use('op-signup') and passport.use('op-login') and so on.
What I think is that I am not using the correct approach for handling users, means the collections don't need to be separated but role based in a single collection. Right ?
Here is the current mongoose models I have right now;
// Member Schema
var MemberSchema = new Schema({
username: String,
password: String,
name: { first: String, last: String },
locality: String,
// and other attributes
});
module.exports = mongoose.model('Member', MemberSchema);
// OperatorSchema
var OperatorSchema = new Schema({
username: String,
password: String,
name: { first: String, last: String },
officeAddress: String,
privatePhone: Number,
// and other attributes related to the operator
});
module.exports = mongoose.model('Operator', OperatorSchema);
Is the above approach correct or like this ?
var UserSchema = new Schema({
username: String,
password: String,
roles: {
member: { type: Schema.Types.ObjectId, ref: 'Member' },
operator: { type: Schema.Types.ObjectId, ref: 'Operator' },
admin: { type: Schema.Types.ObjectId, ref: 'Admin' }
}
});
module.exports = mongoose.model('User', UserSchema);
// and then plug the sub models to this parent one
// Member Schema
var MemberSchema = new Schema({
_user: { type: Schema.Types.ObjectId, ref: 'User' },
name: { first: String, last: String },
locality: String,
// and other attributes
});
module.exports = mongoose.model('Member', MemberSchema);
// OperatorSchema
var OperatorSchema = new Schema({
_user: { type: Schema.Types.ObjectId, ref: 'User' },
name: { first: String, last: String },
officeAddress: String,
privatePhone: Number,
// and other attributes related to the operator
});
module.exports = mongoose.model('Operator', OperatorSchema);
I am quite confused here and in a stuck situation, because when a user state is managed in session after login, the user object is exposed to the request object, and so it can only handle one type of user at a time, and may be member, operator and admin can't log in at the same time from the same browser.
So how do I manage all of these user as different instances in the browser ?
I am quite a newbie in Node.js and coming from a PHP background where managing user states was a breeze :)
What i would do is to add plugins, because you are duplicating username and password field, it is very redundant
models/plugins/member.js
module.exports = function(schema) {
schema.add({
// All the appropriate fields that your member schema need
role: String,
});
}
models/user.js
var member = require(./plugins/member);
var UserSchema = mongoose.Schema({
username: String,
password: String
});
UserSchema.plugins(member);
Later on when you want to check which user could access to which route, use middleware to check it
create this in your passport configuration
exports.requireRole = function(role) {
return function(req, res, next) {
if (req.user && req.user.role === role) next();
else
res.send(404);
}
}
In your route later
app.get('/profile', requireRole('member'), function(req, res) {
// do whatever you want to do
});
app.get('/dashbord', requireRole('operator'), function(req, res) {
// do whatever you want to do
});
There are a lot of ways to implement different access level to a user. This method is one of many.
The best solution would be to use schema inhertiance. That is why we use an ORM like mongoose.
var VehicleSchema = mongoose.Schema({
make : String,
}, { collection : 'vehicles', discriminatorKey : '_type' });
var CarSchema = VehicleSchema.extend({
year : Number
});
var BusSchema = VehicleSchema.extend({
route : Number
})
var Vehicle = mongoose.model('vehicle', VehicleSchema),
Car = mongoose.model('car', CarSchema),
Bus = mongoose.model('bus', BusSchema);
var accord = new Car({
make : 'Honda',
year : 1999
});
var muni = new Bus({
make : 'Neoplan',
route : 33
});
accord.save(function(err) {
muni.save(function(err) {
// vehicles are saved with the _type key set to 'car' and 'bus'
});
})
At this point in MongoDB you will have documents similar to this
{ "_type" : "car", "make" : "Honda", "year" : 1999, "_id" : ObjectId("5024460368368a3007000002"), "__v" : 0 }
{ "_type" : "bus", "make" : "Neoplan", "route" : 33, "_id" : ObjectId("5024460368368a3007000003"), "__v" : 0 }
Source
when querying
Vehicle.find({}, function(err, vehicles) {
console.log(vehicles[0]); // vehicles[0] instanceof Car === true
console.log(vehicles[1]); // vehicles[1] instanceof Bus === true
});
Checkout source / more examples by looking at briankircho little cheatsheet enter link description here
I have two Schemas, and I want to be able to access both of them from the other one.. I am trying to do something like this:
//email.js
var mongoose = require('mongoose')
,Schema = mongoose.Schema
, FoodItemSchema = require('../models/fooditem.js')
, UserSchema = require('../models/user.js').schema
, User = require('../models/user.js').model
console.log(require('../models/user.js'));
var emailSchema = new Schema({
From : String,
Subject : FoodItemSchema,
Body : String,
Date: Date,
FoodItems : [FoodItemSchema],
Owner : { type : Schema.Types.ObjectId , ref: "User" }
});
module.exports = {
model: mongoose.model('Email', emailSchema),
schema : emailSchema
}
//user.js
var mongoose = require('mongoose')
,Schema = mongoose.Schema
, Email = require('../models/email.js').model
, EmailSchema = require('../models/email.js').schema
console.log(require('../models/email.js'));
var userSchema = new Schema({
googleID : String,
accessToken : String,
email : String,
openId: Number,
phoneNumber: String,
SentEmails : [EmailSchema]
// Logs : [{type: Schema.ObjectId, ref: 'events'}]
});
module.exports = {
model : mongoose.model('User', userSchema),
schema : userSchema
}
The first console.log() prints empty string and the second one prints as expected. I feel like I am trying to get the variables in the other schema even before they were created. Is there a common workaround for this? Or should I avoid double dependencies in my design?
Yes, you can create cross-references in Mongoose. But there is no way to create cyclic dependencies in Node.js. Though, you don't need to, because there is no need to require user schema in order to create a reference:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
, FoodItemSchema = require('../models/fooditem.js');
var emailSchema = new Schema({
From: String,
Subject: FoodItemSchema,
Body: String,
Date: Date,
FoodItems: [FoodItemSchema],
Owner: { type: Schema.Types.ObjectId , ref: 'User' }
});
module.exports = {
model: mongoose.model('Email', emailSchema),
schema: emailSchema
}
You can define Schema Add statements to describe common attributes:
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
module.exports = exports = function productCodePlugin(schema, options) {
schema.add({productCode:{
productCode : {type : String},
description : {type : String},
allowed : {type : Boolean}
}});
};
then require the add statement into multiple schema definition files.
var mongoose = require('mongoose')
, Schema = mongoose.Schema
, ObjectId = Schema.ObjectId
, productCodePlugin = require('./productCodePlugin');
var ProductCodeSchema = new Schema({
});
ProductCodeSchema.plugin(productCodePlugin);
Take a look at the following schema:
var userSchema = new mongoose.Schema({
fullName: {
type: String,
required: true,
index: true
},
activationId: {
type: mongoose.Schema.ObjectId
}
});
userSchema.path('activationId').default(function() {
return new mongoose.Schema.ObjectId.fromString('actvt');
});
It is about the "activationId", I want to generate an ID (unique code, objectID prefered since that is already built-in Mongoose) once a user gets created, so if I have the following code:
var newUser = new User({
fullName: 'Rick Richards'
});
newUser.save(function(error, data){
console.log(data.activationId); // Should give the activationId
});
But this solution gives me the following error:
TypeError: undefined is not a function
You can do this by defining a default property for the activationId field that's the ObjectId constructor function:
var userSchema = new mongoose.Schema({
fullName: {
type: String,
required: true,
index: true
},
activationId: {
type: mongoose.Schema.ObjectId
default: mongoose.Types.ObjectId
}
});