Associate one schema with another - mongodb

I have my user schema:
const userSchema = new Schema({
name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
date: { type: Date, default: Date.now },
active: { type: Boolean, default: false }
});
I want to create a leave status schema. Data will be given by only one user about all leave status. I want to associate each one of the leave status to there respective user schema id. I tried leave status schema as the following:
const leaveSchema = new Schema({
leave: [
{
email: { type: String, required: true },
month: { type: String, required: true },
year: { type: Number, required: true },
earnedleave: { type: Number },
sickleave: { type: Number },
festivalleave: { type: Number },
compoff: { type: Number }
}
]
});
How can i associate my emailid with userschema. Can it be possible? If so, how can i tweak it?

We don't need to associate the schema to get leave based on user, You can use aggregation for that,
db.user.aggregate([
...
{$lookup: {from: "leave", localField: "email", foreignField: "leave.email", as: "leaves"}},
...
]);
Still, If you like to associate two collection, You have to use ref using ObjectId
const leaveSchema = new Schema({
leave: [
{
user: { type: Schema.Types.ObjectId, ref: 'user' }, //Your model name as reference
email: { type: String, required: true },
month: { type: String, required: true },
year: { type: Number, required: true },
earnedleave: { type: Number },
sickleave: { type: Number },
festivalleave: { type: Number },
compoff: { type: Number }
}
]
});

Related

How to develop nested condition query in mongoDB

I am pretty new to mongoDb and want to apply nested query.
I have a business schema like this:
const businessSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
businessType: {
type: Schema.Types.ObjectId,
ref: "businessCategory",
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
select: false,
},
review: {
type: [reviewSchema],
},
isDeleted: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
Business has a review where user can do the review and reviewSchema is
const reviewSchema = new mongoose.Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: "users",
required: true,
},
rating: {
type: Number,
enum: [1, 2, 3, 4, 5],
},
reviewArray: {
type: [singleReviewSchema],
},
},
{ timestamps: true }
);
One user can do many reviews, and it has reviewArray.
ReviewArray schema is
const singleReviewSchema = new mongoose.Schema(
{
title: {
type: String,
},
description: {
type: String,
},
isDeleted: {
type: Boolean,
default: false,
},
},
{ timestamps: true }
);
How to fetch the business with a condition business: isDeleted:false and its reviews with singleReviewSchema: isDeleted:false
I dont know your model names, so please replace path with correct names
but it might look like:
businnesModel.find({isDeleted: false})
.populate({
path: 'reviewModelName',
model: 'review',
populate: {
path: 'reviewArray',
model: 'singleReviewModelName',
match: {
isDeleted : false
}
}
})
It should provide you array of businessModel documents - even when their singleReviews array will be empty (because all of reviews are deleted, or there was zero reviews). So you have to filter it out in JS.
To avoid filtering in JS, and to do it a bit more efficient way for mongodb, you can go with aggregate instead.

MongoDB/Mongoose Schema for checking room availability

I've following schema for a Hotel room.
const { Schema, model } = require('mongoose');
const reservationSchema = new Schema({
checkIn: {
type: Date,
require: true
},
checkOut: {
type: Date,
require: true
},
status: {
type: String,
require: true,
enum: ['pending', 'cancel', 'approved', 'active', 'completed']
}
});
const roomSchema = new Schema(
{
title: {
type: String,
required: true
},
slug: {
type: String
},
description: {
type: String,
required: true
},
capacity: {
adults: {
type: Number,
required: true
},
childs: {
type: Number,
default: 0
}
},
roomPrice: {
type: Number,
required: true
},
gallery: [
{
type: String,
require: true
}
],
featuredImage: {
type: String,
require: true
},
reservations: [reservationSchema],
isAvailable: {
type: Boolean,
default: true
},
isFeatured: {
type: Boolean,
default: false
},
isPublish: {
type: Boolean,
default: false
}
},
{ timestamps: true }
);
module.exports = model('Room', roomSchema);
Now I want to find rooms which are not reserved for a particular date period.
Example: If the search query is checkIn: 12/25/2019 and checkOut:12/30/2019 then the query result will show that rooms which are not reserved for this period. Also, it will show the reserved room if the reservation status is canceled.
How can I achieve this?
Do I need to change the Schema design for achieving this?

how to search for any value in the document

I want to search for all value present without specifying key explicitly. Is it possible with this type of schema? If so, how can I write my query. I tried to write query with other refs but I am unable to write without specifying key for each.
const pschema = new Schema({
user: {
type: Schema.Types.ObjectId, // combine by id
ref: "users" //existing model reference
},
company: {
empNo: {
type: Number,
required: true
},
status: {
type: String,
required: true
},...
},
personal: {
gender: {
type: String
},
dob: {
type: Date
},...
},
skills: {
type: [String],
required: true
},
experience: [
{
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
location: {
type: String
}...
}
],
education: [
{
school: {
type: String,
required: true
},
degree: {
type: String,
required: true
},
specialization: {
type: String
},..
}
],
social: {
twitter: {
type: String
},
facebook: {
type: String
},
linkedin: {
type: String
},...
}
});
For example: If i give linkedin url if should search in social-linkedin and same if i give c it should search in skills array

$lookup work in MongoDB but doenst work with mongoose

I have two Documents:
Category = new Schema({
storeKey: { type: String, required: true },
cod: { type: String, required: true },
name: { type: String, required: true },
visible: { type: Boolean }
},
{
timestamps: {
createdAt: "created",
updatedAt: "updated"
}
}
);
and:
Product = new Schema({
name: { type: String, required: true },
cod: String,
storeKey: { type: String, required: true },
categoryId: String,
description: { type: String, required: true },
price: { type: Number, required: true },
stockQuantity: { type: Number, required: true },
avatar: String,
visible: Boolean
}, {
timestamps: true
});
Query on server whith mongoose to locate products with the aggregate category
Product.aggregate([
{
$lookup: {
from: "Category",
localField: "categoryId",
foreignField: "_id",
as: "category"
}
}]
).exec((error, done) => {
if (error) res.status(500).send({
message: langs[req.query.lang],
error
});
res.status(200).send(done);
});
query on local terminal
db.Product.aggregate(
[{
$lookup: {
localField: "categoryId",
from: "Category",
foreignField: "_id",
as: "category"
}
}])
In the terminal, $lookup works correctly. With mongoose, it brings duplicate records and does not bring existing categories. What is wrong?
#Anthony Winzlet was correct, should be categories (in the pural) would have to leave categories (in the pural) and not category, but also, i had not defined the categoryId field as being an ObjectId in the Product Schema, so it was comparing string with ObjectId. In the tests in the terminal I had saved the server return, which returns the _id fields as strings. Now it's working.Thanks!
const ProductSchema = new Schema({
name: { type: String, required: true },
cod: String,
storeKey: { type: String, required: true },
categoryId: { type: Schema.Types.ObjectId, ref: 'categories' },
description: { type: String, required: true },
price: { type: Number, required: true },
stockQuantity: { type: Number, required: true },
avatar: String,
visible: Boolean
}, {
timestamps: true
});

Ordering two reference arrays together

Suppose I have the following schemas:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
});
var QuestionSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
question: { type: String, required: true },
type: { type: String, enum: ['multiple choice', 'multiple select', 'short answer'] },
choices: [String],
answers: [String]
});
var QuestionGroupSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
prompt: { type: String },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }]
});
I am trying to design a way that will allow me to order questions and question groups together.
I was thinking maybe of adding a new field order
var QuizSchema = new mongoose.Schema({
// ...
order: [
{
type: { type: String, enum: ['Question', 'QuestionGroup'] },
id: mongoose.Schema.Types.ObjectId // reference
}
]
});
such that in the database, the field would contain something such as
[
{ type: 'Question', id: ObjectId('57867a34567g67790') },
{ type: 'Question', id: ObjectId('57867a34567g67765') },
{ type: 'QuestionGroup', id: ObjectId('69864b64765y45645') },
{ type: 'Question', id: ObjectId('57867a34567g67770') },
{ type: 'QuestionGroup', id: ObjectId('69864b64767y45647') }
]
This may mean that I would need to "populate" the ordered list of questions and question groups as
quiz.populate('questions questionGroups').exec(function (err, quiz) {
// sort questions and groups by the order
quiz.order = quiz.order.map(function (o) {
if (o.type === 'QuestionGroup') {
return quiz.questionGroups.id(o.id);
}
return quiz.questions.id(o.id);
});
});
So my question: is there a better way to design this?
Virtuals can come in handy here; without persisting order field in db and doing calculations on client each time:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
},
{
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
}
);
QuizSchema
.virtual('order')
.get(function() {
return this.questions.concat(this.questionGroups); //questions followed by questionGroups
});
Sort on createdAt is of course optional, but for that you need to have this field in Question and QuestionGroup:
Quiz.find({}, function (err, quiz) {
//...
})
.populate({path : 'questions', options: {sort: { 'createdAt': 1 }}})
.populate({path : 'questionGroups', options: {sort: { 'createdAt': 1 }}});