In MongoDb, are sub documents references or extensions - mongodb

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.

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.

how to check if information provided is in a different schema

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.

Id is created for every nested objects in documents of a collection in mongodb via mongoose

I have a user schema. While saving document, for every nested object (quizHistory,record & responses) in document, mongoose add _id field automatically. For ref- quizHistory path
const userSchema = new Schema({
firstName: { type: String, required: true ,trim:true},
lastName:{ type: String, required: true ,trim:true},
email: { type: String, unique: true, required: true },
isUser: { type: Boolean, default: true },
password: String,
quizHistory: [{
quizId: { type: Schema.Types.ObjectId, ref: 'Quiz' },
record: [{
recordId:{ type: Number},
startTime: { type: Date },
responses: [{
quesId: { type: Schema.Types.ObjectId, ref: 'Question' },
answers: [Number]
}],
score: Number
}],
avgScore: Number
}]
})
Mongoose create virtual id by default(guide id).
Add this line to your schema.
_id : {id:false}

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()

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.