get data into schema by id in mongoose - node.js

i have two schema i would like to get some info from another schema for example (firstName, lastName) from the userSchema by id that i would provide when adding new comment on the CommentsSchema
const CommentsSchema = new mongoose.Schema({
userId: {
type: String,
required: true
},
commentText: {
type: String,
required: true
},
time: {
type: Date,
default: Date.now
}
});
const UserSchema = new mongoose.Schema({
userName: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
});

You can use mongoose populate to get data from a referenced collection.
First you need to change comments schema to set up a reference to the User model.
You may need to change ref value, if you used a different user model name when you applied mongoose.model("User", UserSchema).
const CommentsSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true
},
commentText: {
type: String,
required: true
},
time: {
type: Date,
default: Date.now
}
});
Let's say you have this user in users collection:
{
"_id": "5e312d5cab2e5028b8865be3",
"userName": "Mongoose",
"firstName": "Buk",
"lastName": "Lau",
"__v": 0
}
And this comment from this user:
{
"_id": "5e312d9cab2e5028b8865be4",
"userId": "5e312d5cab2e5028b8865be3",
"commentText": "this is a comment",
"time": "2020-01-29T07:00:44.126Z",
"__v": 0
}
Now you can access the user info from comment like this:
router.get("/comments/:id", async (req, res) => {
const result = await Comment.findById(req.params.id).populate("userId");
res.send(result);
});
The result will be like this:
{
"_id": "5e312d9cab2e5028b8865be4",
"userId": {
"_id": "5e312d5cab2e5028b8865be3",
"userName": "Mongoose",
"firstName": "Buk",
"lastName": "Lau",
"__v": 0
},
"commentText": "this is a comment",
"time": "2020-01-29T07:00:44.126Z",
"__v": 0
}

Related

MissingSchemaError: Schema hasn't been registered for model images

I keep getting the same error that my imageSchema hasn't been registered for ImageModel when I try to populate the posts from UserModel. But I can't figure out what's the issue. I checked the image Schema file and I don't see anything wrong. Or am I missing something else?
User model
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: true
},
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
min: 6,
max: 30
},
created: {
type: Date,
required: true,
default: Date.now
},
},
{
timestamps: true,
toJSON: {
virtuals: true
}
},
);
userSchema.virtual("posts", {
ref: "ImageModel",
foreignField: 'userId',
localField: '_id'
});
module.exports = mongoose.model('users', userSchema);
Image model
const imageSchema = new mongoose.Schema({
caption: {
type: String,
},
timeCreated: {
type: Date,
default: () => Date.now(),
},
img: {
type: String,
default: 'placeholder.jpg',
},
});
module.exports = mongoose.model("imagesPosts", imageSchema);
model routes
const UserModel = require("../models/User");
const ImageModel = require("../models/Image");
This is the code I'm working on to populate the posts from the User model, but I'm not sure if I'm doing it correctly or not.
const userId = req.user.id;
try {
const result = await UserModel.findById(userId).populate("posts");
console.log("\n\nPopulate result: " + result + "\n\n");
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong, check logs");
}
**Desired output: **
{
"_id": "5e3a885ec511414a3c37a78c",
"username": "Johm",
"email": "john#head.dev",
"password": "123123",
"__v": 0,
"posts": [
{
"_id": "5e3a88e2c511414a3c37a78d",
"caption": "caption one",
"img": "1661309774553spaghetti.jpg",
"userId": "5e3a885ec511414a3c37a78c",
"created": "2020-02-05T09:20:49.754Z",
"__v": 0
},
{
"_id": "5e3a88f1c511414a3c37a78e",
"caption": "caption two",
"img": "1661309774553spaghetti.jpg",
"userId": "5e3a885ec511414a3c37a78c",
"created": "2020-02-05T09:20:49.754Z",
"__v": 0
}
],
}
Declare your posts field as a ref in userSchema:
const userSchema = new mongoose.Schema(
{
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'imagesPosts',
},
],
...
);
You should be able to populate it with:
await UserModel.findById(userId).populate("posts").exec();

Mongoose update subdocument

I have some posts documents which contains an array of ids of comments. I am trying to update a particular comment based on the post and the comment id and not sure how to go about it. I have tried to use the $set command but I think I missed something and It doesn't work. What I have tried doing.
async function updateComment(req, res){
try{
const post = await PostsModel.findOneAndUpdate(
{"postID": req.params.id, "comments.id": req.params.commentID},
{"$set": {
"comments.$": req.body
}}
).populate("comments");
if (!post){
return res.status(404).json({msg: "No post found"});
}
res.status(200).json({post});
}catch (e) {
res.status(500).json(e.message);
}
}
The structure and schema of the document:
{
"_id": "62b2c3d0e88f58ddd1135bc7",
"title": "aaaa",
"content": "check it out",
"createdAt": "2022-06-22T07:23:34.525Z",
"comments": [
"62b2fe23b15d50b496149128"
],
"postID": 1
}
The schema for both the post and comment:
const CommentSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
content:{
type: String,
required: true
},
createdAt:{
type: Date,
default: new Date()
},
})
const PostsSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
createdAt:{
type: Date,
default: new Date()
},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});

How to get model data from nested object

little bit stuck with mongoose. I want to get all users projects where he's added. Tried few options, but in all of them I receive an empty array. So the main question is it possible somehow to find all project by filtering/finding the Project model?
This is how looks my default response to understand what I'm looking for:
{
"_id": "61a8bc4e8e24f10ac7a7288d",
"name": "Random project",
"description": "Random project desc",
"maxWorkingEmployees": 5,
"status": "Paused",
"createdBy": {
"_id": "61a66578f2dabf7555bcf4ab",
"email": "Henrikas#furniture1.eu",
"role": "Owner"
},
"currentlyWorkingEmployees": [
{
"username": "Ema",
"role": "Employee",
"_id": "61a8e0423140ecce769dc971"
}
],
"createdAt": "2021-12-02T12:30:06.461Z",
"updatedAt": "2021-12-02T15:11:51.361Z",
"__v": 0
}
Project model:
const mongoose = require('mongoose');
const SingleUserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
role: {
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);
Here's my controller and my first try:
const getMyProjects = async (req, res) => {
const userId = req.user.userId;
const projects = await Project.find({
currentlyWorkingEmployees: { _id: userId },
});
res.json({projects});
};
Second shot after reading some articles
const getMyProjects = async (req, res) => {
const userId = req.user.userId;
const projects = await Project.aggregate([
{
$match: {
currentlyWorkingEmployees: { _id: userId },
},
},
]);
};
As I said in the comment you can do it accessing to the internal object of the schema with an string accessing to it child object.
Project.find({'currentlyWorkingEmployees._id': userId})

How can i populate multiple paths in my DB?

I want different values from the nested schema. How can I populate them so that every field is showing me its nested data and not the object ID?
I'm using MongoDB and node/express.
This is my postDB where the post schema is defined:
const mongoose = require('mongoose');
var postSchema = new mongoose.Schema({
title: {
type:String,
required:true
},
body: {
type:String,
required:true
},
comments:[{
type: mongoose.Schema.Types.ObjectId,
ref: "comment"
}],
category:{
type:String,
required:true
},
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: "user"
}
},{timestamps : true}
)
module.exports = mongoose.model('postData', postSchema);
This is my commentDB which is referenced from the postDB:
const mongoose = require('mongoose');
// Using the Schema constructor, create a new CommentSchema object
// This is similar to a Sequelize model
var CommentSchema = new mongoose.Schema({
// `body` is of type String
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: "user"
},
body: String
},{timestamps : true});
var Comment = mongoose.model("comment", CommentSchema);
module.exports = Comment;
This is how I'm trying to populate:
router.get('/READ', (req,res)=>{
posts.find({}, function (err, post) {
if (err) {
console.log(err);
}else{
res.json({post})
}
}
)
.populate([{path:'creator'}, {path:'comments'}])
})
However the results i get from this does not populate every object ID.
For example:
{
"comments": [
{
"_id": "5f8d91d8f8550044f0f755c8",
"creator": "5f84e5b1d893ac42dcc9cb78",
"body": "This looks cool",
"createdAt": "2020-10-19T13:17:12.323Z",
"updatedAt": "2020-10-19T13:17:12.323Z",
"__v": 0
},
{
"_id": "5f8d92e82ecfbe34b8f6375b",
"creater": "5f84e5b1d893ac42dcc9cb78",
"body": "hello",
"createdAt": "2020-10-19T13:21:44.463Z",
"updatedAt": "2020-10-19T13:21:44.463Z",
"__v": 0
},
],
"_id": "5f887cef6fd7d34548a592ea",
"title": "A DESCRIPTIVE PARAGRAPH EXAMPLE",
"body": "\"The room in which I found myself was very large and lofty. The windows were ",
"category": "Finance",
"creator": {
"joined": "2020-10-15T12:14:23.888Z",
"posts": [
"5f887cef6fd7d34548a592ea",
"5f887e0d6fd7d34548a592ec",
"5f887e266fd7d34548a592ed",
"5f887e586fd7d34548a592ee",
"5f89bfccc2bebd40b07b044a",
"5f89c36e906bbb27b84af897",
"5f89c7614199d52b141ff249",
"5f89c7ea4199d52b141ff24a",
"5f8c5ab175ef762ed89eddba",
"5f8c5be2d7fac046f0021d9f"
],
"_id": "5f88481d00ed460960da90f8",
"username": "kenwaysharma",
"email": "kenwaysharma#gmail.com",
"password": "$2b$10$p3qjmdSKWIF9qAagZoqbjuG34cjOgXTe5XYER0aowwviIS65COVlu",
"__v": 0
},
"__v": 0,
"updatedAt": "2020-10-20T05:42:56.320Z"
}
Here is the userDB:
username: {
type: String,
required: [true, "Username is required!"],
unique: true,
lowercase: true,
},
email:{
type: String,
required: [true, "Email is required!"],
unique: true,
lowercase: true,
validate: [isEmail, "Please enter a valid email!"]
},
password:{
type: String,
required: [true, "Password is required!"],
minlength: [6, "Enter atleast 6 characters!"],
},
comments:[{
type: mongoose.Schema.Types.ObjectId,
ref: "comment"
}],
posts:[{
type: mongoose.Schema.Types.ObjectId,
ref: "postData"
}],
},{timestamps : true});
GET users:
router.get('/USERS', (req,res)=>{
User.find({}, function (err, user) {
if (err) {
console.log(err);
}else{
res.send(user)
}
}
).populate('comments') .populate('posts')
})
How do I get the creator data inside of comments instead of just its object ID?
Update:
I also tried selecting the creator inside comments like
.populate('comments', 'creator')
but it still gives me the creator object ID in a string.
Update 2:
I have added the code for userDB to which the commentDB and postDB references.
Also added the GET users just to see how it works in postman.
Try chaining multiple populate methods and using the exec method to pass your callback.
posts.find({})
.populate({
path: 'comments',
populate: {
path: 'creator',
model: 'user'
}
})
.populate('creator')
.exec(function (err, post) {
if (err) {
console.log(err);
}else{
res.json({post})
}
});

Include only the path of Mongoose schema which is relevant to value of another path

Let's say I have a Schema like this:
Schema({
username: String,
privilege: {
type: String,
enum: ["admin", "pro", "user", "guest"],
required: false,
default: "user"
},
tagType: { // A little badge that appears next to users' names on the site.
///???
}
});
For the tagType, I would like to be able to define something like this if privilege is admin:
{
color: String,
image: String,
text: {
type: String,
required: true,
enum: ["I'm the highest rank", "I'm an admin", "Admin privileges are great"]
},
dateAdded: Date
}
Meanwhile, if privilege is pro:
{
color: String,
text: {
type: String,
required: false,
enum: ["This website is great", "I'm am a Pro user", "I get a nice tag!"],
default: "This website is great"
},
dateAdded: Date
}
Meanwhile, for all user or guest entries, no option should be allowed.
Is this sort of dynamic cross-checking possible? I have tried reading through the docs, but despite things that seem much more complex being in there, I couldn't find any way to achieve this.
You can implement this by using Discriminators
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema(
{
username: String,
privilege: {
type: String,
enum: ["admin", "pro", "user", "guest"],
required: false,
default: "user"
}
},
{ discriminatorKey: "privilege" }
);
const User = mongoose.model("User", userSchema);
User.discriminator(
"admin",
new Schema({
tagType: {
color: String,
image: String,
text: {
type: String,
required: true,
enum: ["I'm the highest rank","I'm an admin","Admin privileges are great"]
},
dateAdded: { type: Date, default: Date.now }
}
})
);
User.discriminator(
"pro",
new Schema({
tagType: {
color: String,
text: {
type: String,
required: false,
enum: ["This website is great","I'm am a Pro user","I get a nice tag!"],
default: "This website is great"
},
dateAdded: { type: Date, default: Date.now }
}
})
);
module.exports = User;
With this model and schema you can create a user with tagType it will be ignored.
Sample code to create user:
const User = require("../models/user");
router.post("/user", async (req, res) => {
let result = await User.create(req.body);
res.send(result);
});
Sample request body:
{
"username": "user 1",
"tagType": {
"color": "red",
"text": "I'm an admin"
}
}
Response:
{
"privilege": "user",
"_id": "5dfa07043a20814f80d60d6b",
"username": "user 1",
"__v": 0
}
Sample request to create an admin: (note that we added "privilege": "admin")
{
"username": "admin 1",
"privilege": "admin",
"tagType": {
"color": "red",
"text": "I'm an admin"
}
}
Response: (note that tagType is saved)
{
"_id": "5dfa07a63a20814f80d60d6d",
"privilege": "admin",
"username": "admin 1",
"tagType": {
"color": "red",
"text": "I'm an admin",
"dateAdded": "2019-12-18T11:04:06.461Z"
},
"__v": 0
}

Resources