Dynamic Mongoose Schema based on property - node.js

I have the following user schema (where all distinct properties of different user types are consolidated):
var UserSchema = new mongoose.Schema({
status: String,
firstName: String,
lastName: String,
address: Object,
email: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/\S+#\S+\.\S+/, 'is invalid'], index: true},
organization: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Organization' }],
phone: {type: Number, unique: true, required: true, required: [true, "can't be blank"]},
role: String,
hash: String,
salt: String,
deliverySchedule: [{type: String, required: true}]
}
"Common" Schema (what all user types share in common):
var UserSchema = new mongoose.Schema({
status: String,
firstName: String,
lastName: String,
email: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/\S+#\S+\.\S+/, 'is invalid'], index: true},
phone: {type: Number, unique: true, required: true, required: [true, "can't be blank"]},
role: String,
hash: String,
salt: String
}
Role = "Customer":
address: [{type: Object, required: true}]
Role = "DeliveryMan":
deliverySchedule: [{type: String, required: true}]
organization: [{ type: mongoose.Schema.Types.ObjectId, required: true,
ref: 'Organization' }],
Role = "Seller":
organization: [{ type: mongoose.Schema.Types.ObjectId, required: true,
ref: 'Organization' }],
I would like to add (and REQUIRE if possible) some fields to the "common" schema based on the user's role. However, I want to store them in the same collection.
How can I add a method to my models/Users.js to add properties to the schema based on "user.role"

Make require validation optional for each role dependent fields.
var UserSchema = new mongoose.Schema({
status: String,
firstName: String,
lastName: String,
email: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/\S+#\S+\.\S+/, 'is invalid'], index: true},
phone: {type: Number, unique: true, required: true, required: [true, "can't be blank"]},
role: {type: String, enum: ['Customer', 'DeliveryMan', 'Seller'], required: true},
address: {type: [Object], required: isRequired("address")},
deliverySchedule: {type: [String], required: isRequired("deliverySchedule")},
organization: { type: [mongoose.Schema.Types.ObjectId], ref: 'Organization', required: isRequired("organization")},
hash: String,
salt: String
});
function isRequired(field){
return function(){
if(field == "address"){
return this.role === "Customer"
}
else if(field == "deliverySchedule"){
return this.role === "DeliveryMan"
}
else if(field == "organization"){
return this.role === "Seller" || this.role === "DeliveryMan"
}
}
};

Related

find if reference object id is contained in the array mongodb

I'm trying to find whether a given EventID exists in the attendsTo array of a user. Is there any way to check for this condition in mongoose? Thanks a lot!
User Schema:
const userSchema = mongoose.Schema(
{
username: {
type: String,
required: [true, 'Please add a username'],
},
email: {
type: String,
required: [true, 'Please add an email'],
unique: true,
},
password: {
type: String,
required: [true, 'Please add a password'],
},
language: {
type: String,
default: "English"
},
attendsTo: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Event' }],
friends: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
pendingFriends: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
images:[{data:Buffer,contentType: String}],
profilePic: [{data:Buffer, contentType:String}],
status: String,
},
{
timestamps: true,
}
)

Index of username_1 gets created in mongoDB although no username in the Schema/Model

Hello, I hope this question ain't stupid, but I have a problem with Nodejs/mongoose/MongoDB.
var SportsSchema = new Schema({
title: String,
internalTitle : String,
semester : String ,
uniqueIdent : { type : String, unique : true, index : true },
sportsType: String,
kindOfSport: String,
description: String,
location: String,
numberOfParts: Number,
recurrence: String,
weekDay1: {type: String, default: 'empty'},
weekDay2: {type: String, default: 'empty'},
startTime1: {type: String, default: 'empty'},
startTime2: {type: String, default: 'empty'},
sportDates: [{
type: Date
}],
specDates: [{
type: String
}],
timeFrame: [{
type: Date
}],
created: {type: Date, default: Date.now },
});
This is the Schema. Only uniqueIdent is unique and indexing. As soon as the nodeJS app starts, the mongoDB collection has the following indexes: _id, uniqueIdent and username. While the first two are expected, the third one is flabbergasting me.
There is a username in the other schema that i am using, but that shouldn't influence this one, should it.
Any help would be appreciated, if more information is necessary, I will happily provide it.
other schema:
var UserSchema = new Schema({
username: {type: String, lowercase: true, unique: true, match: [/\S+#\S+\.\S+/, 'ist nicht erlaubt']},
email: {type: String, lowercase: true, unique: true, match: [/\S+#\S+\.\S+/, 'ist nicht erlaubt']},
firstname :String,
lastname: String,
password: String,
//admin: {type: Boolean, default: false },
permissionLevel: {type: String, default: 'user' },
tr_tmp: {type: Boolean, default: 'false'},
classOne: {type: String, default : 'empty' },
classTwo: {type: String, default : 'empty' },
classThree: {type: String, default : 'empty' }
});

Mongoose populate by email

On the backend I have two Schemas:
User:
var UserSchema = new mongoose.Schema({
isActive: {type: Boolean, default: false},
isVerified: {type: Boolean, default: false},
firstName: {type: String, required: [true, "can't be blank"]},
lastName: {type: String, required: [true, "can't be blank"]},
email: {type: String, lowercase: true, unique: true, required: [true, "can't be blank"], match: [/\S+#\S+\.\S+/, 'is invalid'], index: true},
phone: {type: Number, unique: true, required: true, required: [true, "can't be blank"]}, // validate phone
role: {type: String, enum: ['invalid', 'precustomer', 'customer', 'broker', 'brokerAdmin', 'dealer']},
hash: String,
salt: String,
address: {type: Object, required: [isRequired('address'), "can't be blank"]}, //require customer
organization: { type: mongoose.Schema.Types.ObjectId,
ref: 'Organization',
required: [isRequired('organization'), "can't be blank"]}, //require broker && dealer
deliverySchedule: String // for now.. make this custom obj soon --- OPTIONAL
}, {timestamps: true});
Application:
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
var ApplicationSchema = new mongoose.Schema({
customer: {type: mongoose.Schema.Types.ObjectId, ref: 'User', required: [true, "can't be blank"]},
dealer: {type: mongoose.Schema.Types.ObjectId, ref: 'User', required: [true, "can't be blank"]},
broker: {type: mongoose.Schema.Types.ObjectId, ref: 'User', required: [true, "can't be blank"]},
files: {type: mongoose.Schema.Types.ObjectId, ref: 'Files', required: [true, "can't be blank"]},
}, {timestamps: true});
mongoose.model('Application', ApplicationSchema);
};
On the front end, in order for a "Broker" to create an "Application," he must first add a "customer" to the application. To do this, a Broker will search for a user by email, and the server can respond with either:
email exists (User Object = id, email, ...)
No user found ({})
It would not be possible to store an _id in "Create Application" API endpoint, because when a "Broker" creates an "Application", the "customer" is sent a link in their email to register (new _id), and the _id linked to the Application is lost (as far as I know.) I would like my "customer" "dealer" and "broker" to be String instead of mongoose.Schema.Types.ObjectId, however I would like it to work with .populate('customer dealer broker') using email instead of ID.
Would it make more sense to remove the required validation for name, phone, etc from the User Schema and only require an email for "registration" and request the rest from the "customer" after clicking the confirmation link?

Schema Association in Mongoose

I have 2 models:
Here is the User Model:
const userSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
passwordResetToken: String,
passwordResetExpires: Date,
facebook: String,
twitter: String,
tokens: Array,
profile: {
name: String,
gender: String,
location: String,
website: String,
picture: String
}
}, { timestamps: true });
And here is the Revive Model:
const reviveSchema = new mongoose.Schema({
reviveShowName: {type: String, required: true},
reviveTitle: {type: String, required: true},
reviveCategory: {type: String, required: true},
reviveGoal: {type: Number, required: true},
revivePhoto: {type: String, required: true},
reviveVideo: {type: String},
reviveStory: {type: String},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
name: String
}
}, { timestamps: true });
In the Revive model, I'm trying to the reference the author and get the author's id and that works... How do I also get the name from profiles -> name...? Clearly name: String is wrong...
Mongoose relations work, based on the ref and type value of the nested object. In your case you have associated the id property of author to point to the User model.
If you want to populate the author with the user information, you should just do :
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
Then in your query you just use populate
Revive.find({})
.populate( 'author' )
.exec( function( error, docs ) {
console.log( docs ); // will have `[{author:{profile:{...}}}]` data
} );

BrainTree node integration

I have the most Odd problem I have ever had. I just simple cannot find what is wrong with my code.
this is my user model
var userSchema = new Schema({
firstName: {type: String, required: true, trim: true},
lastName: {type: String, required: true, trim: true},
address: {
street: {type: String, required: true, trim: true},
city: {type: String, required: true, trim: true},
state: {type: String, min: 2, max: 2, required: false},
zipCode: {type: String, required: true, trim: true}
},
customerId: String,
subscription: {
id: String,
status: String,
planId: String,
price: Number,
nextBillingDate: Date
},
creditCard: {
token: {type: String, required: true}
},
email: {type: String, required: true, unique: true, trim: true},
password: {type: String, required: true, trim: true},
phone: {type: String, required: true, trim: true},
birthDate: {type: Date, required: true, trim: true},
role: {staff: Boolean, admin: Boolean, sAdmin: Boolean},
companies:[
{
type: Schema.Types.ObjectId,
ref: 'Company',
required: true
}
],
edited: {type: Date},
created: {type: Date, default: Date.now}
});
Now im creating a User Admin on my application with this code.
// BrainTree API
var nonce = req.body.payment_method_nonce;
gateway.customer.create({
firstName: user.firstName,
lastName: user.lastName,
company: req.body.compName,
email: req.body.email,
paymentMethodNonce: nonce
}, function (err, result) {
if(err) return handleError(err);
//NO ERROR FOUND
user.customerId = result.customer.id;
gateway.subscription.create({
paymentMethodToken: result.customer.paymentMethods[0].token,
planId: 'myId'
}, function (err, result) {
if(err){
throw err;
}
else {
//save subscription info
user.subscription.id = result.subscription.id;
user.subscription.status = result.subscription.status;
user.subscription.planId = result.subscription.status;
user.subscription.price = result.subscription.price;
user.subscription.nextBillingDate = result.subscription.nextBillingDate;
//transaction info
t.id = result.subscription.transactions[0].id;
t.amount = result.subscription.transactions[0].amount;
t.creditCard.maskedNumber = result.subscription.transactions[0].creditCard.maskedNumber;
t.created = result.subscription.transactions[0].createdAt;
saveAdmin();
}
});
now on user.customerId = result.customer.id;
I get this console error "Cannot read property 'id' of undefined", but that is not true. when I do a console.log(result.customer.id) I get the Id. I just don't know why is throwing the error.
I don't know if anybody can help me with this because but you never know.
Thanks in advance.
I solved the problem the return handleError(err) some how was executing the request twice. so I change the return handleError(err) with return console.error(err) and it solved the problem.

Resources