Mongoose schema with field of an array of other schemas - node.js

first of all, I am a beginner and currently, I am working on a social media blog type.
Now, I have my userSchema and postSchema models:
USER MODEL
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please insert your name'],
},
email: {
type: String,
required: [true, 'Please insert your email'],
unique: true,
lowercase: true, //transform into lowercase / not validator
validate: [validator.isEmail, 'Please provide a valid email'],
},
avatar: {
type: String,
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
password: {
type: String,
required: [true, 'Please provide a password'],
minLength: 8,
select: false,
},
passwordConfirm: {
type: String,
required: [true, 'Please confirm your password'],
validate: {
validator: function (el) {
return el === this.password;
},
message: 'Passwords are not the same',
},
},
passwordChangedAt: Date,
posts: [] // ???????????????
});
POST MODEL
const postSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'A post must have a title'],
},
author: {
type: String,
required: [true, 'A post must have a title'],
},
likes: {
type: Number,
default: 10,
},
comments: {
type: [String],
},
image: {
type: String,
},
postBody: {
type: String,
required: [true, 'A post must contain a body'],
},
createdAt: {
type: Date,
default: Date.now(),
select: false,
},
});
Now I don't know if it's the best approach but I was thinking of having a field in userSchema with the type of an array of postSchema so I will have for each user their own posts created. Can I do that? If not how I can achieve that?
Should I use search params fields to filter posts by the author? I am really confused how I should approach this situation. Thank you guys

Check out this example
const UserSchema = new mongoose.Schema({
username: String,
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}]
})
const PostSchema = new mongoose.Schema({
content: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
})
const Post = mongoose.model('Post', PostSchema, 'posts');
const User = mongoose.model('User', UserSchema, 'users');
module.exports = { User, Post };
Credit: https://medium.com/#nicknauert/mongooses-model-populate-b844ae6d1ee7

Related

how to correctly structure mongoose (mongoDb) model with relationships?

I am new to backend development, I want to build a comment system which supports replies. I am stuck on how to structure my mongoose model
I am looking at having three models
one for users, one for the post, and one for comments
for user
import mongoose from "mongoose";
const UserSchema = new mongoose.Schema({
name: { type: String, required: [true, "Name is required"] },
email: { type: String, required: [true, "email is required"] },
password: {
type: String,
required: [true, `password is required`],
},
isAdmin: {
type: Boolean,
default: false,
required: true,
},
image: { type: String, required: [true, `please provide a photo`] },
});
export default mongoose.models.User || mongoose.model("User", UserSchema);
for post
import mongoose from "mongoose";
const PostSchema = new mongoose.Schema(
{
title: {
type: String,
required: [true, "Please provide a name for this Article"],
},
description: {
type: String,
required: [true, `please provide a description for this article],
},
image: {
type: String,
required: true,
},
keywords: {
type: Array,
required: true,
},
body: {
type: String,
required: [true, `please provide a body for this article],
},
author: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
},
{ timestamps: true }
);
export default mongoose.models.Post || mongoose.model("Post", PostSchema);
for comment
import mongoose from "mongoose";
const CommentSchema = new mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: "Author" },
comment: { type: String,
required: [true, `please provide a message for this comment`],}
replies: {type: Array}
});
export default mongoose.models.Comment || mongoose.model("Comment", CommentSchema);
I am stuck, every reply for comment is itself a comment, how do I relate the reply to the comment?
also, how do I relate the comment and the post?

I want to join two schemas into one schema in nodejs mongodb

I want to join two schemas into one because they both have almost same attributes and i want to use it for PostSchema that a user and also an owner can create and show their posts but i dont know how to achieve it
for example :
UserSchema
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
gender: String,
role: {
type: String,
default: "User",
},
});
OwnerSchema
const OwnerSchema = new mongoose.Schema({
name: String,
gender: String,
email: String,
password: String,
role: {
type: String,
default: 'Owner'
},
restaurant: RestaurantSchema
})
PostSchema
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
body: {
type: String,
required: true,
},
photo: {
type: String,
required: true,
},
postedBy: {
type: ObjectId,
ref: *Combined user and owner*,
},
comments: [CommentSchema],
}, { timestamps: true });
Get rid of your OwnerSchema - and modify your UserSchema to something like the below:
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
gender: String,
role: {
type: String,
default: "User", //could be Owner when applicable
},
restaurant: RestaurantSchema, //could be null when role=User
});

Sub-schema properties are not updated mongoose

little bit stuck with one issue which is related to mongoose. I have a Project schema with sub-schema(SingleUserSchema). Whenever I add a new User to this schema it saves everything and that's ok. The issue is that if I update the user, old values in schema does not update. Any ideas how to solve this case? Stuck here for a whole day already.
Schema:
const mongoose = require('mongoose');
const SingleUserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
role: {
type: String,
required: true,
},
status: {
type: String,
required: true,
},
});
const ProjectSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, 'Provide project name'],
minlength: 5,
},
description: {
type: String,
required: [true, 'Provide description about the project'],
},
maxWorkingEmployees: {
type: Number,
required: [
true,
'Provide maximum number of employees working on this project',
],
},
currentlyWorkingEmployees: [SingleUserSchema],
status: {
type: String,
enum: ['Pending', 'In progress', 'Paused', 'Delayed', 'Completed'],
default: 'Pending',
},
createdBy: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
},
{ timestamps: true }
);
module.exports = mongoose.model('Project', ProjectSchema);
Controller:
const attachEmployeeToProject = async (req, res) => {
const { projectId, userId } = req.params;
const project = await Project.findOne({ _id: projectId });
const user = await User.findOne({ _id: userId });
if (!user) {
throw new NotFoundError(`User with id ${userId} does not exists`);
}
if (!project) {
throw new NotFoundError(`Project with id ${userId} does not exists`);
}
const { role, email, status } = user;
const SingleUserSchema = {
email,
role,
status,
};
let currentlyWorkingEmployees = [
...project.currentlyWorkingEmployees,
SingleUserSchema,
];
req.body.currentlyWorkingEmployees = currentlyWorkingEmployees;
const updateProject = await Project.findOneAndUpdate(
{ _id: projectId },
req.body,
{
new: true,
runValidators: true,
}
);
res.status(StatusCodes.OK).json({ updateProject });
};
Maybe I just simply need to create a reference? Tried like this and received tons of errors, also felt lost how to create n references in array.
currentlyWorkingEmployees: [
{ type: mongoose.Schema.ObjectId, ref: 'User', required: true },
],
User schema:
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: [true, 'Please provide username'],
minlength: 3,
maxlength: 50,
},
email: {
type: String,
required: [true, 'Please provide username'],
unique: true,
validate: {
validator: validator.isEmail,
message: 'Please provide valid email address',
},
},
password: {
type: String,
required: [true, 'Please provide password'],
validator: {
validate: {
validator: validator.isStrongPassword,
message: 'Please provide stronger password',
},
},
},
firstname: {
type: String,
required: [true, 'Please provide first name'],
},
lastname: {
type: String,
required: [true, 'Please provide last name'],
},
status: {
type: String,
enum: ['Working', 'Pause', 'Offline'],
default: 'Offline',
},
role: {
type: String,
enum: [
'Developer',
'Analyst',
'Designer',
'Architect',
'Project Manager',
'Owner',
'Teamleader',
'Employee',
],
default: 'Employee',
},
verificationToken: {
type: String,
},
isTokenVerified: {
type: Boolean,
default: false,
},
tokenValidationDate: {
type: Date,
default: null,
},
});
First, as far as I see you don't have a model called 'User' so that's the reason why your reference is not working.
currentlyWorkingEmployees: [
{ type: mongoose.Schema.ObjectId, ref: 'User', required: true },
]
https://mongoosejs.com/docs/populate.html
Second, you need to identify the user you want to update (based on userId) inside currentlyWorkingEmployees collection, if I understood correctly your problem.
Hope it helps!

How to make array ref in Mongoose

Here actually I want to make the service collection that contain the array of references of the ratings. when a user rate a service than an element is pushed in the array containing reference of user , service ID no and the rating.
Service Model like this:
var ServiceSchema = new Schema({
user_id:{
type: String,
required: [true, 'please provide user id']
},
name: {
type: String,
required: [true, 'please enter your name']
},
rating : [{ type: Schema.Types.ObjectId, ref: 'rating' }],
});
Rating schema:
var RatingSchema = Schema({
S_id : { type: Schema.Types.ObjectId},
Rating : Number,
By : { type: Schema.Types.ObjectId}
});
user schema:
var UserSchema = new Schema({
id: {
type: String,
unique: true,
required: [true, 'please enter your id']
},
password: {
type: String,
required: [true, 'please enter your password']
},
name: {
type: String,
required: [true, 'please enter your name']
},
type: {
type: [{
type: String,
enum: ['visitor', 'seller']
}],
default: ['visitor']
},
});
and I have defined the export as:
module.exports = mongoose.model('user', UserSchema, 'users');
module.exports = mongoose.model('service', ServiceSchema, 'service');
module.exports = mongoose.model('rating', RatingSchema, 'rating');
I want to make a function called rate but I am not getting how to make it.
exports.rate = function(req, res) {
var curr_service = new Service(req.body, result);
new_service.save(function(err, service) {
if (err)
res.send(err);
res.json(service);
});
};
So far I have done this.
Can someone help me to understand what should I do now? because I haven't find that much about mongoose to add ref in array...
In my case. This error was happening because instead of putting {es_indexed: true} inside the object declaration, I was putting it in the object that was using. For example:
const Client: Schema({
name: {type: String, es_indexed: true},
address: {type: Adress, es_indexed: true} //Wrong, causing error
})
Adress: Schema({
address: {type: String},
zipCode: {type: Number}
})
The correct way to use, is putting es_indexed: true into primitive types inside "Adress" schema declaration.
const Client: Schema({
name: {type: String, es_indexed: true},
address: {type: Adress} //Right way
})
Adress: Schema({
address: {type: String, es_indexed: true},
zipCode: {type: Number, es_indexed: true}
})
I hope it was helpful

How to make some fields not updatable once saved in mongoose?

I have build a schema as follows:
const UserInfoSchema = new Schema({
email: { type: String, required: true, unique: true },
username: { type: String, required: true, unique: true },
userId: { type: Schema.Types.ObjectId, ref: 'User'},
displayName: { type: String, required: true },
profilePic: {
filename: {type: String},
url: {type: String}
},
created_at: Date,
updated_at: Date
})
What I need here is once the fields such as email, username and userId are saved, should not be modified. Is there anything pre-build in mongoose for this kind of feature?
I have done some research on schema.pre('update', (next) => {}), but got nothing really useful/don't know if one can use for the mentioned feature. Any help on this matter is greatly appreciated. Thanks in advance.
There is an easier way
when you save the Schema, you can set the field as immutable, like this
const UserInfoSchema = new Schema({
email: { type: String, required: true, unique: true, immutable:true },
username: { type: String, required: true, unique: true, immutable:true },
userId: { type: Schema.Types.ObjectId, ref: 'User', immutable:true},
displayName: { type: String, required: true },
profilePic: {
filename: {type: String},
url: {type: String}
},
created_at: Date,
updated_at: Date
})
it won't throw any error, if you want it you should check it elsewhere, but when you try to modify the immutable fields, it wont be changed at all
for(const key in userUpdates) {
switch(key) {
case 'username':
case 'email':
throw new Error('These field/s cannot be changed anymore');
}
}
User.findByIdAndUpdate(id, userUpdates, { new: true, runValidators: true });

Resources