12

I am building a kind of social network for students and teachers with MEAN stack.

Students has their own account page different from the one of the teachers.

I have one single registration page for both teachers and students.

When registering, the user has to choose the "type" of the account with a select html tag (student or teacher),

I want to have one single model representating two different type of user (teacher and student) that contains common fields like email and password etc, and some specific fields depending on the select tag ; because I am using email-verification-node npm and it only permits to use a single model in the PersistentUserModel function.

user3711521
  • 285
  • 5
  • 14

2 Answers2

10

I suggest use this approach.

You should have a separate Schemas for Account, Teacher and Student so the different info between the teachers and students should not be mixed in one place.

Account

var Account = new Schema({
    email:String,
    password:String,
    _teacher:{type:Schema.Types.ObjectId, ref:'Teacher'},
    _student:{type:Schema.Types.ObjectId, ref:'Student'}
})

Under account, you should reference the Teacher model if teacher account otherwise reference Students model.

To check if Account is Teacher or Student you could just check _teacher, if it has a value then it's a Teacher account else it's a student. But to make the condition more unique you, check both _teacher and _student.

This approach will save you a lot of refactoring in the future if you will decided to allow the teacher to be a student as well (which is not impossible to happen), he/she can just use the same account and register as a student. Just like what google is doing, on account/email multiple types of app to use.

Teacher

var Teacher = new Schema({
    name:{type:Schema.Types.ObjectId, ref:'Name'}
    // Other teachers info
})

Student

var Student = new Schema({
    name:{type:Schema.Types.ObjectId, ref:'Name'}
    // Other students info
})

Name

On this part maybe you are wondering why you need a separate model for name. Well that is because in this approach you can just use one route or endpoint or query to search users across your app. When you search for a name, all students and teachers with matching result will be queried without looking into 2 different collections (Teacher collection and Student collection).

On good use case for this is, for sure you will have an admin dashboard where you can manage all students and teachers. In that dashboard you can just one search field for both teacher and student.

var Name = new Schema({
    firstName:String,
    middleName:String,
    lastName:String
})

Good reads

Other tips

You could also separate the Address like I did with the name here. Reason? Same purpose as with the Name, you might want to add search by location feature or something like that.

I hope this helps.

jofftiquez
  • 7,548
  • 10
  • 67
  • 121
8

Set up a Boolean field inside your data model that has the key teacher (or student if you prefer) and set that when the user signs up.

Edit:

You can have two different schemas, one for each user type. Declaring them would look something like this.

const usersSchema = new Schema({/* base schema */});

const teachersSchema = new Schema({ /* teachers schema */});
const studentsSchema = new Schema({ /* students schema */});

And

const User = mongoose.model('User', usersSchema, 'users');
User.Teacher = mongoose.model('Teacher', teachersSchema, 'users');
User.Student = mongoose.model('Student', studentsSchema, 'users');

Have a look at the docs here

Edit 2:

I found a better way of doing this is using discriminators...thanks!

const options = {discriminatorKey: 'kind'};

const userSchema = new mongoose.Schema({/* user schema */}, options);
const User = mongoose.model('User', userSchema);

// Schema that inherits from User
const teacherSchema = User.discriminator('Teacher',
  new mongoose.Schema({/* Schema specific to teacher */}, options));
const studentSchema = User.discriminator('Student',
  new mongoose.Schema({/* Schema specific to student */}, options));

const teacher = new Teacher({/* you can add everything here! */});
const student = new Student({/* you can add everything here! */});

Look up by calling either Teacher or Student

Now you have one model with two Schema! More info in the docs here.

Edit with more info:

You would create two types of data structure, a teacher and student which would both be held in the User collection. When you are calling the database you call using Teacher or Student.

Any data that is going to be common to both is put in the User schema, while anything that is specific you put in the relevant schema.

When you receive a call to the api you direct to the relevant look up. You could use a boolean or a string in the request params, and then separate out the logic with an if statement or a switch statement. I would use a string and a switch.

In your client set two constants TEACHER = 'teacher', STUDENT = 'student', and call with the relevant constant to the api in the body of your request. That way when it hits the api the request will be parsed to the correct lookup and send back the relevant data.

Kamran
  • 103
  • 5
alex
  • 5,467
  • 4
  • 33
  • 43
  • and after that? how can I dynamically allocate the specific information of the user with the right mongoose schema ? – user3711521 Jul 28 '16 at 15:14
  • I tried your code which is working fine, I'm not very familiar with mongodb so please can you tell me how I can link a User with User.Teacher ? I put my common props in User (email and pass ) and tried this : newuser= new User.Student(); newuser.email=someemail; newuser.somespecificfield=someval; and when I saved it it didn't set the email field but just what is specific to User.Student – user3711521 Jul 29 '16 at 01:49
  • I tell you what I understand from your solution and please correct me if this is wrong : I have to define in my BasSchema some field named type , and when the user registers I create a User Object , and set commun info and type field to a bool (for example 0 if he's a student) , then also create User.Student(), and finally do some linking between User object and User.Student object since the two entries belong to the same user – user3711521 Jul 29 '16 at 02:07
  • thank you it's working ,but just one thing, is it possible to use one data structure , I mean something like this : var singleDataStruct= new GenericModel(); and then this datastruct can hold whether techear fields or student fields. I think it's impossible to change the props after instanciating the model ,just want to be sure (?) – user3711521 Jul 29 '16 at 04:01
  • Everything will be held in the one collection User, the document schema of the teacher will be different from the student but otherwise they are in the same collection. Using different schema will enable you to search depending on the schema type. I think what you are asking with the single data structure is actually a collection. Each document is part of the collection, but the documents can have different schema. Does that answer your question? – alex Jul 29 '16 at 05:48
  • This is of course the reason why mongodb is simpler to do this, as opposed to a relational database. – alex Jul 29 '16 at 05:50
  • 1
    Did you mean to use `User.discriminator` instead of `Event.discriminator`? Unless there is some magic happening inside `options` it seems as though the `userSchema` and the `teacherSchema`/`studentSchema` are not at all linked. – Davide Lorino Sep 08 '19 at 02:38