I have a ProfileSchema that I want to keep track of posts that have been posted. When I save a new Post it has a Profile_id but when I fetch the profile entry the posts array is empty. How does this work?
Profile Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ProfileSchema = new mongoose.Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
handle: {
type: String,
required: true,
max: 40
},
posts: [
{
type: Schema.Types.ObjectId,
ref: 'posts'
}
],date:{
type: Date,
default: Date.now
}
})
module.exports = Profile = mongoose.model('profile', ProfileSchema);
Post Schema
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
profile: {
type: Schema.Types.ObjectId,
ref: 'profile'
},
title: {
type: String,
required: true,
max: 40
},
body: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
})
module.exports = Profile = mongoose.model('posts', PostSchema)
how I save it
router.post('/', passport.authenticate('jwt', {session: false}), (req,res) => {
const newPost = new Post({
title: req.body.title,
body: req.body.body,
img: req.body.img,
user: req.user._id,
profile: req.body.profile_id
});
newPost.save().then(post => res.json(post));
})
I'm a bit confused the mongoose documentation uses syntax I'm not familiar with or haven't learned their way yet. my user populates when I fetch for the profile data but the posts array is blank when I populate it.
Related
/I want to create a module section for a course website for which I will need a lesson schema as well so How can I design lesson schema , module schema , and course schema so they
work just how they are needed to workCurrently I am doing this/
import mongoose from 'mongoose'
const LessonSchema = new mongoose.Schema({
title: String,
content: String,
resource_url: String
})
const ModuleSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
lessons: [LessonSchema]
})
export const Module = mongoose.model('Module', ModuleSchema);
const CourseSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: 'Name is required'
},
price: {
type: String,
trim: true,
required: true
},
image: {
data: Buffer,
contentType: String
},
intro: {
type: String,
required :true
},
description: {
type: String,
trim: true
},
category: {
type: String,
required: 'Category is required'
},
updated: Date,
created: {
type: Date,
default: Date.now
},
instructor: {type: mongoose.Schema.ObjectId, ref: 'User'},
published: {
type: Boolean,
default: false
},
modules: [ModuleSchema]
})
export default mongoose.model('Course', CourseSchema)
Above was the schema and this is logic
const newLesson = async (req, res) => {
try {
let lesson = req.body.lesson
let course = await Course.find({modules: {_id: req.params.moduleId}})
console.log(course)
} catch (err) {
return res.status(400).json({
error: errorHandler.getErrorMessage(err)
})
}
}
const newModule = async (req, res) => {
try {
let lesson = req.body.lesson
let result = await Course.findByIdAndUpdate(req.course._id, {$push: {modules: {name: req.body.name, lessons: lesson}}, updated: Date.now()}, {new: true})
.populate('instructor', '_id name')
.exec()
res.json(result)
} catch (err) {
return res.status(400).json({
error: errorHandler.getErrorMessage(err)
})
}
}
**I have been brainstorming this from a while and cant get through it do you know how can I shape the schema and logic so that I can push lessons in module and then module in course schema ? **
I'm trying to populate my post's author fields (which is are object ids) with the corresponding author objects which are in a separate collection.
My controller code is as follows:
exports.readPosts = async (req, res) => {
try {
const posts = await Post.find({ board: req.params.board });
await posts.populate("author").execPopulate();
res.send(posts);
} catch (err) {
res.status(400).send(err.message);
}
};
I'm at a loss as to why this isn't working as I have very similar code in another controller method that is working just fine.
All help greatly appreciated.
Below is the relevant Model file:
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
trim: true,
},
content: { type: String, required: true, trim: true },
comments: [
{
comment: {
type: String,
required: true,
trim: true,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
date: {
type: Date,
default: Date.now(),
},
},
],
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
board: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Board",
},
},
{ timestamps: true }
);
const Post = mongoose.model("Post", postSchema);
module.exports = Post;
posts is an array of models. populate must be called on a model. The preferred way to do this is at query time. It probably works on your other controller because you are using a findOne so it is returning the model, not the Array.
const posts = Post
.find({ board: req.params.board })
.populate('author')
.exec();
Say I have one model, Book, and another model, Genre. When I create the book, I'd like to be able to pass a Genre ID and have the model automatically fetch and embed the document. For example:
const bookSchema = new Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
genre: {
type: ObjectId,
required: true,
}
});
const genreSchema = new Schema({
name: {
type: String,
required: true,
},
});
Then I'd like to be create a book as follows:
const Book = await Book.create({
title: 'Lord of the Rings',
author: 'J. R. R. Tolkien',
genre: '5d6ede6a0ba62570afcedd3a',
});
That would create a book and automatically embed the genre document from the given ID. Is there a way to do that from within the schema, or would I have to wrap it in additional logic?
You can use the pre-save mongoose middleware/hook to find the genre and set it as an embedded document. In mongoose pre-save hook, this will be the current document, you can read the value and set the value to this object before it is written to the database.
Note that, since this is a pre-save hook, it will be run only on Model.create() or document.save(). So it won't be run on Model.insertMany(). But it will be run when you update the document using document.save(). If you want to set the genre only on new documents, you will have to check for this.isNew property
const { Schema, Types } = mongoose
const genreSchema = new Schema({
name: {
type: String,
required: true,
},
});
const Genre = mongoose.model('Genre', genreSchema)
const bookSchema = new Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
genreId: {
type: Schema.Types.ObjectId,
required: true,
},
genre: {
type: genreSchema,
},
});
bookSchema.pre('save', async function() {
if (this.isNew) { // this.isNew will be true on only new documents
this.genre = await Genre.findById(this.genreId) // `this` is the new book document
}
})
const Book = mongoose.model('Book', bookSchema)
/* Test book creation */
const genre = await Genre.create({
name: 'Fantasy'
})
const book = await Book.create({
title: 'Lord of the Rings',
author: 'J. R. R. Tolkien',
genreId: genre._id,
});
console.log(book)
you can use mixed schema type and document middleware to solve your problem.see my sample code below:
const genreSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
});
const Genre = mongoose.model('Genre', genreSchema);
const bookSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
genre: {
type: Object,
required: true,
}
});
bookSchema.pre('save', async function () {
const genreID = mongoose.Types.ObjectId(this.genre);
this.genre = await Genre.findById(genreID);
});
const Book = mongoose.model('Book', bookSchema);
const newBook = new Book({ title: 'The book', author: 'xyz', genre: '5ef55c67be27fb2a08a1131c' });
newBook.save();
How do you know which genre ID to embed? Can you send this from your frontend?
If yes, then simply select the genre ID from you frontend and then pass it in your API's request body.
While in your backend:
router.route('/book')
.post((req, res) => {
Book.create({
title: req.body.title,
author: req.body.author,
genre: req.body.genre,
}, (err, product) => {
if (err) {
res.send(err);
} else {
res.json({success:true})
}
});
})
Do something like this to create a new book object in your Book collection.
If I understand your question correctly I think what you're looking for is populate. https://mongoosejs.com/docs/populate.html
It would change your schema to look like the following
const bookSchema = new Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
genre: {
type: Schema.Types.ObjectId,
ref: 'Genre',
required: true,
}
});
const genreSchema = new Schema({
name: {
type: String,
required: true,
},
});
When you get your book you can reference the genre by doing this
Book.find()
.populate('genre')
Hopefully, that answered your question!
I have a simple express app where users can log in and post pictures of mountains. I'm having an issue saving the posts the users add. Specifically, the user id that is referenced from another schema(user schema). When I submit the form, I get an undefined error on the "._id" and I'm not sure how to go about fixing it.
Below are my model schemas.
const userSchema = new Schema({
name: String,
email: String,
username: String,
password: String,
});
const canyonSchema = new Schema({
canyon: String,
image: String,
description: String,
cost: Number,
createdAt: { type: Date, default: Date.now },
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
const Canyon = mongoose.model("Canyon", canyonSchema);
module.exports = Canyon;
const User = mongoose.model('User', userSchema);
module.exports = User
Here is the logic that is supposed to save the information to mongo. When I run, I get a "._id undefined". Any help.
const User = require('../models/user');
const Canyon = require('../models/Canyon');
router.post("/add", , function(req, res){
if(req.body.canyon &&
rea.body.image &&
req.body.description){
const newCanyon = {
canyon: req.body.canyon,
image: req.body.image,
description: req.body.description,
author: {
id: req.user._id,
}, username: req.user.username
};
Canyon.create(newCanyon, function(error, canyon){
if(error){
return next(error)
} else {
req.session.userId = user._id;
return res.redirect('/profile')
}
});
} else {
const err = new Error('All fields required.');
err.status = 400;
return next(err);
}
});
In your model schema, instead of
const canyonSchema = new Schema({
canyon: String,
image: String,
description: String,
cost: Number,
createdAt: { type: Date, default: Date.now },
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
Make the author itself the reference:
const canyonSchema = new Schema({
canyon: String,
image: String,
description: String,
cost: Number,
createdAt: { type: Date, default: Date.now },
author: { type: mongoose.Schema.ObjectId, ref: 'User', index: true },
});
To create a canyon object,
const newCanyon = {
canyon: req.body.canyon,
image: req.body.image,
description: req.body.description,
author: req.user._id
};
To get the user information embedded in the canyon object when query on canyons, just populate the author in the query,
Canyon.findById(id).populate('author').exec()
These the response for user that Im getting from get request to profile API
"user": "5cc3a4e8d37a7259b45c97fe"
What I'm looking for instead is
"user":{
"_id": "5cc3a4e8d37a7259b45c97fe",
"name":"Jhon Doe",
}
Here is my code:
Profile.findOne({
user: req.user.id
})
.populate('user',['name']) // I added this line to populate user object with name
.then(profile=>{
if(!profile){
errors.noprofile = 'There is no profile for this user'
return res.status(404).json(errors);
}
res.json(profile)
})
.catch(err => res.status(404).json(err));
However, Im getting these error:
{
"message": "Schema hasn't been registered for model \"users\".\nUse mongoose.model(name, schema)",
"name": "MissingSchemaError"
}
What am I missing
Profile Schema
const ProfileSchema = new Schema({
user:{
type: Schema.Types.ObjectId,
ref: 'users'
},
handle: {
type: String,
required: true,
max: 40
},
company: {
type: String
},
website: {
type: String,
}
})
Here is how my Users schema looks like
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
name:{
type: String,
required: true,
},
email:{
type: String,
required: true,
},
password:{
type: String,
required: true,
},
avator:{
type: String,
},
date:{
type: Date,
default: Date.now,
}
});
module.exports = User = mongoose.model('Users', UserSchema)
Schema that you are referencing in Profile schema is users, but you have saved your user schema as Users. So I would say that you need to update your Profile schema:
const ProfileSchema = new Schema({
user:{
type: Schema.Types.ObjectId,
ref: 'Users'
},
handle: {
type: String,
required: true,
max: 40
},
company: {
type: String
},
website: {
type: String,
}
})
Name under which is saved your User schema can be found in this line
module.exports = User = mongoose.model('Users', UserSchema)
The error says you don't have Schema for Users. You reference it from Profile Schema, but you don't have it. It can be this way:
const Users = new Schema({
name: String
})