how to check if information provided is in a different schema - mongodb

On sign up page, I want to check if the email, first name and last name entered by the user is in the organization schema or not, if it's in the database then create a new user
my user schema:
const UserSchema = new mongoose.Schema(
{
organization: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Organization'
},
firstName: {
type: String,
required: [true, "First name is required"],
},
lastName: {
type: String,
required: [true, "Last name is required"],
},
email: {
type: String,
required: [true, "Email is required"],
validate: {
validator: (val) => /^([\w-\.]+#([\w-]+\.)+[\w-]+)?$/.test(val),
message: "Please enter a valid email",
},
},
password: {
type: String,
required: [true, "Password is required"],
minlength: [8, "Password must be 8 characters or longer"],
},
},
{ timestamps: true }
)
organization schema:
const OrganizationSchema = mongoose.Schema({
orgname: {
type: String
},
domain: {
type: String
},
users: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User' }
],
timestamp: {
type: Date,
default: Date.now
},
})
the answer might be easy to some but I am new to MongoDB

I think there's a problem in your conception.
How are you supposed to find user's firstname and lastname in the organisation entity?
The relation here is the following :
an Organization can have multiple users
a User can have an Organization
If you want your organization to be used a a "filter" for the signup process, you could do the following:
have an Organization schema that holds a whitelist of domain and (firstname / lastname) -> you can have a collection but it would duplicate a lot of information,
have a User schema that is linked to Organization like you did in your question.
You can optimize Organization schema by splitting it (to prevent info duplication for the orgname and domain) :
Schema Organization (name, domain)
Schema Whitelist (link to organization, firstname, lastname)
Schema User (link to organization)
Tell me if you need me to clarify something.

Related

Mongoose Nested Schema - required fields inside array field

I have following mongoose schema -
const UserSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
email: {type: String, required: true, trim: true},
password: {type: String, required: true, trim: true},
addresses: [{
type: {type: String, required: true, trim: true},
pinCode: {type: String, required: true, trim: true},
city: {type: String, required: true, trim: true},
state: {type: String, required: true, trim: true},
landmark: {type: String, required: false, trim: true},
}]
})
only name, email and password are required for registration. User can have more than one address , and each address has some required fields like in flipkart.com/account/addresses user's account page.
Each address will be stored in addresses array field in mongodb database.
I want to keep addresses array [ ] when user register. address are only provided through user's account page web page.
But I am getting ValidatorError because schema of doc inside addresses array having required fields.
when user register -
{
name: 'rahul',
email: 'rahul#example.com',
password: 'pass123',
addresses: []
}
user is already registered, and then add address
{
name: 'rahul',
email: 'rahul#example.com',
password: 'pass123',
addresses: [
{
type: 'home',
pinCode: '111222',
city: 'city1',
state: 'state1'
},
{
type: 'work',
pinCode: '333444',
city: 'city2',
state: 'state2',
landmark: 'landmark2'
}
]
}
How to achieve above ??
Any help would be appreciated.
You just have to remove required:true field from address array not even required:false.
Just type and trim will the job and required is false by default so you dont have to include it.
I was doing this -
const userDoc = new UserModel({
name: nm,
email: em,
password: pass,
addresses: []
})
Not passing addresses: [], prevent error ValidatorError
const userDoc = new UserModel({
name: nm,
email: em,
password: pass
})
Try the below code. Hope this will help you.
const ToySchema = new mongoose.Schema({ name: String });
const ToyBoxSchema = new mongoose.Schema({
toys: [ToySchema],
bucketName: String,
numToys: Number
});
const ToyBox = mongoose.model('ToyBox', ToyBoxSchema);
var toyBoxData = {
toys : [
{ name : "Dummy Toy 1"},
{ name : "Dummy Toy 2"},
{ name : "Dummy Toy 3"},
],
bucketName: "My Toy Bucket",
numToys: 3
}
var toyBucket = new ToyBox(toyBoxData);
toyBucket.save( (err: any)=> {
console.log(err);
})
And when you will run the above code, you will be able to see a collection and document like this in your DB, as in the below screenshot.
Hope this will help you. For more information please visit this mongoose documentation link.

Is there any way in mongoose to access other Schema fields and update those fileds when a "document" is saved into the DB?

I'm currently working on a Library Management System and I'm using MongoDB as my Database so in there I have 4 schemas
User 2) Book 3) Review 4) bookIssue (which handles all the book
issuing)
I'll mention just my User and Book Issue schemas here coz I only want help regarding these two Schemas,
bookIssueHistory: { type: Array, default: null, }
Whenever a book is issued via "bookIssue" Schema I want to store that book's "id" in to the "bookIssueHistory" array (mentioned above) which is in my "userSchema", so I've mentioned both of my schemas below:
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please enter your name'],
},
email: {
type: String,
required: [true, 'Please enter your email'],
unique: true,
lowercase: true,
validate: [validator.isEmail, 'Please enter a valid email'],
},
photo: String,
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
password: {
type: String,
required: [true, 'Please enter your password'],
minlength: 8,
select: false,
},
passwordConfirm: {
type: String,
required: [true, 'Re-Enter your password'],
validate: {
validator: function (el) {
return el === this.password;
},
message: 'Entered password and confirmed password do not match',
},
},
passwordChangedAt: Date,
passwordResetToken: String,
passwordResetExpires: Date,
noOfBooksIssued: {
type: Number,
default: 0,
},
currentlyIssuedBooks: {
type: Number,
max: [3, 'You are only allowed to issue 3 books at a time'],
default: 0,
},
bookIssueHistory: {
type: Array,
default: null,
},
active: {
type: Boolean,
default: true,
select: false,
},
});
my book issue schema looks like this:
const bookIssueSchema = mongoose.Schema({
issuedAt: {
type: Date,
default: Date.now,
},
totalIssues: {
type: Number,
default: 0,
},
book: {
type: mongoose.Schema.ObjectId,
ref: 'Book',
required: [true, 'issue must belong to a book.'],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [true, 'issue must belong to a user.'],
},
});
You can use mongoose middleware, in particular the pre-save hook to run some logic before bookIssue get inserted into the database.
bookIssueSchema.pre('save', function () {
// you can access the current document to be saved by `this`
if (this.isNew) { // apply to new bookIssue only
await this.model('User').findByIdAndUpdate(this.user, {
$addToSet: { bookIssueHistory: this.book } // use $addToSet to ensure distinct values, otherwise use $push
})
}
})
Important: The pre-save hook will be run only when you use BookIssue.create() or bookIssue.save() and not when you run BookIssue.insertMany()

In MongoDb, are sub documents references or extensions

Using mongoose you can create a sub document as such:
export const UserSchema = new Schema({
email: {
type: String,
unique: true,
required: "email is required.",
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']
},
firstName: {
type: String,
required: "firstName is required."
},
lastName: {
type: String,
required: "lastName is required."
},
dateOfBirth: {
type: Date,
required: "dateOfBirth is required."
},
roles: [{
role: String,
required: "role is required",
validate: isValidUserRole
}],
address: AddressSchema,
});
Address Schema
export const AddressSchema = new Schema({
streetAddress: {
type: String
},
addressLine2: {
type: String
},
city: {
type: String
},
state: {
type: String
},
zipCode: {
type: String
},
country: {
type: String
}
});
Whenever I save a user, will mongoose create a new user document with nested address data or a user and address and then reference that address document within the user instance?
How would I approach the case of 2 users sharing the same address?
Thanks!
You need not to provide the AddressSchema in the UserSchema schema.
You can just provide reference of AddressSchema like the following.
export const UserSchema = new Schema({
email: {
type: String,
unique: true,
required: "email is required.",
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']
},
firstName: {
type: String,
required: "firstName is required."
},
lastName: {
type: String,
required: "lastName is required."
},
dateOfBirth: {
type: Date,
required: "dateOfBirth is required."
},
roles: [{
role: String,
required: "role is required",
validate: isValidUserRole
}],
address: {type:mongoose.Schema.Types.ObjectId,ref:'AddressSchema',required:true}
});
But remember that in order to make this work, 1st you need to create an Address data and provide their _id in UserSchema.
What happens by the above method is,
AddressSchema's ObjectId(_id) works as the REFERENCE in UserSchema. Basically, it acts as a REFERENCE DOCUMENT in UserSchema
You can find this out by querying your data through mongo shell.
When a document contains an embedded document (this is indistinguishable from nested hashes in the shell), the embedded document wholly belongs to the top level document and cannot be included-by-reference into another document.

Mongoose: reference between models with same schema keys

As a front-end developer, I would like to have some isomorphic object for two mongoose models.
Let's say I have a user profile:
const profileSchema = new Schema({
firstName: { type: String },
lastName: { type: String },
// example of difference between model schemas
__user: { type: ObjectId, ref: 'Users' },
}
And I would like to create a list of Contact, where each contact will have some of the same keys:
const contactSchema = new Schema({
firstName: { type: String },
lastName: { type: String },
__profile: {
type: ObjectId,
ref: 'Profiles',
unique: true,
},
comment: { type: String },
}
NOTE: Contact could be the both:
as a reference to the Profile
and as independent record in DB / document.
==============================
My question: which is the best way to, organize models on such a way, so
contact could be a reference to the profile
when similar Profile key, like firstName will be updated, contact firstName will be updated too
AVOID of next ref
await Contact.findById(SOME_ID).populate('__profile');
// result
{
firstName: '',
lastName: '',
__profile: {
firstName: 'Chuck',
lastName: 'Norris',
}
}
Desired result - keep contact "isomorphic" like:
{
firstName: 'Chuck', // the key value from profile
lastName: 'Norris', // the key value from profile
__profile: SOME_PROFILE_ID,
}
Is this possible?
P.S: in my app, I'm using refs and started to use discriminators approaches.
I'd approach this task either:
1) put all data inside 1 collection (e.g. Profile):
// Profile model
{
firstName: 'Chuck',
lastName: 'Norris',
contacts: [{
type: ObjectId,
ref: 'Profile',
unique: true,
}],
...all other properties
}
that way you will be able to store just contacts (e.g. when I want to add just a contact) and profiles with much more info.
2) or will use discriminators to create a base class (e.g. Contact) and build Profile model upon it:
const options = { discriminatorKey: 'userType' };
const Contact = mongoose.model('Contact', new mongoose.Schema({
firstName: String,
lastName: String
},
options)
)
const Profile = Contact.discriminator(
'Profile',
new mongoose.Schema(
{
contacts: [{
type: ObjectId,
ref: 'Contact',
unique: true,
}],
comments: []
},
options
)
);
that way you will be able to save Contacts and Profiles inside 1 collection and reference base class (Contact) for contacts inside Profile
Hope that helps!
In my case, complete usage of Mongoose discriminators did not give me an advantage, because discriminators give you an opportunity to:
They enable you to have multiple models with overlapping schemas on
top of the same underlying MongoDB collection.
As a result, by using discriminators approach, I will reive one collection
of:
profiles
And there will be a mix of users and contact profiles.
==============================
So I decided to use two approaches:
create BesaSchema for the profile
take advantage of Mongoose Subdocuments
RESULT:
// keys which are same for both user Profile and Contact
const Schema = require('mongoose').Schema;
const util = require('util');
function BaseProfileSchema(...args) {
Schema.apply(this, args);
this.add({
firstName: { type: String },
lastName: { type: String },
});
}
util.inherits(BaseProfileSchema, Schema);
// user Profile Model
const profileSchema = new BaseProfileSchema({
__user: {
type: String,
ref: 'users',
required: true,
unique: true,
},
});
const Profile = mongoose.model('profiles', profileSchema);
// Contact with profile as subdocument
const contactProfileSchema = new BaseProfileSchema();
const contactSchema = new Schema({
// Associations
__refProfile: {
type: Schema.Types.ObjectId,
ref: 'profiles',
index: {
unique: true,
sparse: true,
},
},
profile: contactProfileSchema,
});
const Contact = mongoose.model('contacts', contactSchema);
As a result, I'm having DB with next collections:
users
profiles
contacts
Both profiles and contacts.profile are IDENTICAL because I'm extending the base shared schema.
Moreover:
inside Contact I'm having different keys for real referenced profile (__refProfile which can NOT be edit by others) and contact.profile
profile inside connection can be edited ONLY when the contact was edited by itself
P.S: happy codding 👨‍💻🍻

how to database design for user authentication like student, teacher , super admin

I am trying to create different types of registration for user . I have got three collection for users . I have been references user collection in both of teacher and student because I need to get email and password.
If a teacher register including email, password, firstname , lastname etc , there is a collection .
if a student register including email, password, firstname , lastname etc , there is another collection .
our all of email and password will be one collections
user - table/collection
- email : test#gmail.com
- password: asdfasdf
student - table /collection
- firstname: 'sujon"
teacher - table/collection
- firstname: "sujon"
const UserSchema = mongoose.Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
default: false,
},
})
const StudentSchema = mongoose.Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
firstname: {
type: String,
},
lastname: {
type: String,
},
photo: {
type: String,
},
education: {
type: String,
},
birth: {
type: Date,
},
sex: {
type: Boolean,
},
})
const TeacherSchema = mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId,
ref: "user"
},
firstname: {
type: String
},
lastname: {
type: String
},
photo: {
type: String
},
designation: {
type: String
},
birth: {
type: Date
},
sex: {
type: Boolean
}
});
how can implement database design
Creating a single User schema would be fine. You can have a single schema with all properties (since all three types of user have almost same properties) and then add a 'roles' field like this:
roles: [{ type: String }]
The roles field can have multiple roles [ 'Teacher', 'Student' ... ]. In this way a single user can have multiple roles for example a Teacher can also be an admin etc.
Also you won't have to create a new model whenever a new role is introduced.
Users can be queried based on their roles. The roles can then be used for authentication as well for example a user with role 'Teacher' can create assignment or a user with role 'Student' can submit assignments. When registering a user you can set some sort of model validation on the api or client side to accept a certain model.