populate embedded document on mongoose nodejs - node.js

I'm currently working with nodeJs and mongoose and I want to populate a sub-document
This is my main model
var postSchema = require('./post.js');
var blogSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
postSchema: [postSchema]
});
var blog = mongoose.model('blog', blogSchema);
module.exports = blog;
this is the post sub-document
var mongoose = require('mongoose');
var postSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
rol: {
type: String,
required: true
}
});
var post = mongoose.model('post', post);
module.exports = post;
And I want to add several 'post' inside the 'blog' schema, but I don't know how, I tried populating it by I'm now doing it properly! I read about 'populate' on mongoose but I'm not understanding it at all, can someone explain it?

You cannot use arrays of external Eschemas in mongoose. You must define them in the same file, or just save the array of _id´s and create a collection appart.
Check the mongoose docs. They talk about that: http://mongoosejs.com/docs/2.7.x/docs/embedded-documents.html
var mongoose = require('mongoose');
var blogPost = new mongoose.Schema({
name: {
type: String,
required: true
},
rol: {
type: String,
required: true
}
});
var blog = new mongoose.Schema({
name: {
type: String,
required: true
},
postList: [blogPost]
});
mongoose.model('blog', blogSchema);
module.exports = blog;
var Blog = mongoose.model('blog');
// create a blog post
var theBlog = new Blog();
// create a comment
theBlog.postList.push({ title: 'My comment' });
theBlog.postList.save(function (err) {
if (!err) console.log('Success!');
});
Hope it may help you

Related

What is wrong with my React.js code for blog comment feature

I created a comment feature in my node.js blog application. I am using Mongodb data base. Everything works fine in postman. I can create new comments. However, I am trying to replicate the same in my client side via React.js. I think I have exhausted all my ideas. Here is the error message I keep getting each time I try to create a new comment from client side: This error is console.log from the catch(err) in backend.
undefined
CastError: Cast to ObjectId failed for value ":id" (type string) at path "_id" for model "Post"
Post Model
//creating the user models for the database
const mongoose = require("mongoose"); //import mongoose
const Schema = mongoose.Schema;
const PostSchema = new mongoose.Schema(
{
title:{
type: String,
required: true,
unique: true,
},
description:{
type: String,
required: true,
},
postPhoto:{
type: String,
required:false,
},
username:{
type: Schema.Types.ObjectId,
ref: 'User'
},
categories:{
type: Array,
},
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
unique: true,
}]
}, {timestamps: true},
);
//exporting this schema
module.exports = mongoose.model("Post", PostSchema); //the module name is "Post"
Comment Model
const mongoose = require("mongoose"); //import mongoose to be used
const Schema = mongoose.Schema;
const CommentSchema = new mongoose.Schema(
{
commentdescription:{
type: String,
required: true,
},
author:{
type: Schema.Types.ObjectId,
ref: 'User',
},
}, {timestamps: true}
);
//exporting this schema
module.exports = mongoose.model("Comment", CommentSchema); //the module name is "Post"
create new comment and push to post codes
//creating catergory logic
router.post("/posts/:id/comment", async (req, res) =>{
const newComment = new Comment(req.body);//we create a new comment for the database
try{
//we need to try and catch the new comment and save it
const currentPost = await Post.findByIdAndUpdate(req.params.id)//we need to find the post that has the comment via the id
currentPost.comments.push(newComment)//we need to push the comment into the post
await newComment.save();
await currentPost.save()//we saved the new post with the comment
res.status(200).json(currentPost)
}catch(err){
console.log(err)
res.status(500).json(err)
}
})
What I have written in React
const [commentdescription, setCommentDescription] = useState('')
const [postId, setPostId] = useState()
//function to create comment
const handleComment = async ()=>{
const newComment = {
_id: postId,
author: user._id,
commentdescription,
};
try{
await axios.post("/posts/:id/comment", newComment);
}catch(err){
console.log(err)
}
}
Everything works fine in postman. I know I am missing something in react.
You need to pass actual ID to that POST request url.
await axios.post(`/posts/${postId}/comment`, newComment);
When POSTing to /posts/:id/comment, your parsed value (req.params.id) will literally be :id, which cannot be casted as ObjectId.

I can't handle populate in mongoose

I think it's not populating categories couse when i try log 'category' in console i get
"ReferenceError: category is not defined". For me it is like in docs but as we see it's not. Is anyone can tell me what is wrong??
//model/Category.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const CatSchema = new Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true
}
});
mongoose.model("categories", CatSchema, "categories");
model/Story.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const StorySchema = new Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true
},
category: {
type: Schema.Types.ObjectId,
ref: "categories"
}
});
mongoose.model("stories", StorySchema, "stories");
routes/stories.js
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Category = mongoose.model('categories');
const Story = mongoose.model('stories');
router.get('/add', (req, res) => {
Story.find()
.populate('category', 'title')
.then(stories => {
res.render('stories/add', {
stories: stories
});
});
});
Query.prototype.populate() returns a Query object on which you need to run .exec(), try this:
Story.find({})
.populate('category', 'title')
.exec()
.then(stories => {
res.render('stories/add', {
stories: stories
});
});
It was problem with that how I want to use it. Code is working

Accessing Schema.Types.ObjectId's values with Mongoose

Hi I'm learning MongoDB and how to use Mongoose,
I'm trying to build a simple Workout manager app, where workouts have a name (Eg: "Upper Body"), an email account (which is used to identify who created the workout) and array of exercise consisting of references to exercises.
I want to be able to have a query where I can access the exercise title(Eg: "Push Ups") by using just the workout name.
My workout.js Model:
var mongoose = require('mongoose');
const Schema = mongoose.Schema;
var Exercise = require('./exercise');
const workoutSchema = new Schema({
title: {type: 'String', required: true},
email: {type: 'String', required: true},
exercises: [{ type: Schema.Types.ObjectId, ref: 'Exercise' }],
});
module.exports = mongoose.model('Workout', workoutSchema);
My exercise.js Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var exerciseSchema = new mongoose.Schema({
title: {type: String, required: true}
}, {
timestamps: true
});
module.exports = mongoose.model('Exercise', exerciseSchema);
My Workout Controller looks like this :
var Workout = require('../models/workout');
var Exercises = require('../models/exercise');
exports.getWorkoutExercises = function(req, res) {
Workout.findOne({ title: req.params.workout_name})
.populate('exercises')
.exec((err, exercises) => {
if (err){
res.send(err);
}
/* I want to be able to return or access the exercises title */
res.json({exercises.title});
});
}
In My routes.js i have this :
workoutRoutes.get('/:workout_name', WorkoutController.getWorkoutExercises);
Any Help or tips is much appreciated !
I am not sure what you did here res.json({exercises.title}); Can you try this? Hope it works
Workout.findOne({ title: req.params.workout_name})
.populate('exercises')
.exec((err, workout) => {
if (err){
res.send(err);
} else {
res.send({ exercises: workout.exercises });
}
});
And then, on your client side, you can iterate the exercises array and access to the title of exercise

Mongoose one-to-many

can you explain me how to organize mongoose models to create one to many connections? It is needed keep separate collections.
suppose i have stores and items
//store.js
var mongoose = require('mongoose');
module.exports = mongoose.model('Store', {
name : String,
itemsinstore: [ String]
});
//item.js
var mongoose = require('mongoose');
module.exports = mongoose.model('Item', {
name : String,
storeforitem: [String]
});
Am i doing it in the right way?
And how to access pass data to arryas?
Here is the code yo enter name to item. But how to enter id to array of id's (itemsinstore)?
app.post('/api/stores', function(req, res) {
Store.create({
name: req.body.name,
}, function(err, store) {
if (err)
res.send(err);
});
})
You should use model reference and populate() method:
http://mongoosejs.com/docs/populate.html
Define your models:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var storeSchema = Schema({
name : String,
itemsInStore: [{ type: Schema.Types.ObjectId, ref: 'Item' }]
});
var Store = mongoose.model('Store', storeSchema);
var itemSchema = Schema({
name : String,
storeForItem: [{ type: Schema.Types.ObjectId, ref: 'Store' }]
});
var Item = mongoose.model('Item', itemSchema);
Save a new item into an existing store:
var item = new Item({name: 'Foo'});
item.save(function(err) {
store.itemsInStore.push(item);
store.save(function(err) {
// todo
});
});
Get items from a store
Store
.find({}) // all
.populate('itemsInStore')
.exec(function (err, stores) {
if (err) return handleError(err);
// Stores with items
});
You can do using the best practices with Virtuals.
Store.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const StoreSchema = new Schema({
name: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
}
})
StoreSchema.virtual('items', {
ref: 'Item',
localField: '_id',
foreignField: 'storeId',
justOne: false // set true for one-to-one relationship
})
module.exports = mongoose.model('Store', StoreSchema)
Item.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const ItemSchema = new Schema({
storeId: {
type: Schema.Types.ObjectId,
required: true
},
name: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
}
})
module.exports = mongoose.model('Item', ItemSchema)
StoreController.js
const Store = require('Store.js')
module.exports.getStore = (req, res) => {
const query = Store.findById(req.params.id).populate('items')
query.exec((err, store) => {
return res.status(200).json({ store, items: store.items })
})
}
Keep in mind that virtuals are not included in toJSON() output by default. If you want populate virtuals to show up when using functions that rely on JSON.stringify(), like Express' res.json() function, set the virtuals: true option on your schema's toJSON options.
// Set `virtuals: true` so `res.json()` works
const StoreSchema = new Schema({
name: String
}, { toJSON: { virtuals: true } });
Okay, this is how you define a dependancy:
var mongoose = require('mongoose');
module.exports = mongoose.model('Todo', {
name : String,
itemsinstore: [{ type: Schema.Types.ObjectId, ref: 'Item' }]
});
And make sure you have different names:
var mongoose = require('mongoose');
module.exports = mongoose.model('Item', {
name : String,
storeforitem: [String]
});
Keep an eye on Item in both cases.
And then you just want to pass the array of ObjectIDs in it. See more here: http://mongoosejs.com/docs/populate.html
Try this:
Store.findOne({_id:'5892b603986f7a419c1add07'})
.exec (function(err, store){
if(err) return res.send(err);
var item = new Item({name: 'Foo'});
item.save(function(err) {
store.itemsInStore.push(item);
store.save(function(err) {
// todo
});
});

cant get data from database after multiple schema declared (mongoose + express + mongodb

I'm new to node.js and I am having problem accessing to the when multiple mongoose schema were declare.
//schema.js in model
var mongoose = require('mongoose');
var Schema = mongoose.Schema
, ObjectId = Schema.ObjectId;
//User Schema
var userSchema = new Schema({
id: ObjectId,
firstname: {type: String, require: true},
lastname: {type: String, require: true},
username: {type: String, unique: true, require: true},
password: {type: String, require: true},
role: {type: [String], require: true}
})
var User = mongoose.model('User', userSchema);
module.exports = User;
//Question Schema
var qnSchema = new Schema({
id: ObjectId,
question: {type: String, require: true},
module_id: {type: ObjectId, ref: 'Module'}
})
var Question = mongoose.model('Question', qnSchema);
module.exports = Question;
//Answer Schema
var ansSchema = new Schema({
id: ObjectId,
answer: String,
question: {type: ObjectId, ref: 'Question'}
})
var Answer = mongoose.model('Answer', ansSchema);
module.exports = Answer;
//Module Schema
var modSchema = new Schema({
id: ObjectId,
name: {type: String, require: true}
})
var Module = mongoose.model('Module', modSchema);
module.exports = Module;
//Role Schema
var roleSchema = new Schema({
id: ObjectId,
role: {type: String, require: true}
})
var Role = mongoose.model('Role', roleSchema);
module.exports = Role;
//index.js in controller
var mongoose = require('mongoose');
var User = require('../models/schema');
var db = mongoose.connect('mongodb://localhost/damai');
module.exports = function(app) {
app.get('/', function(req, res) {
if (typeof req.session.userid == 'undefined') {
res.render('login', { title: app.get('title') });
} else {
res.render('index', { title: app.get('title') });
}
});
app.post('/login', function(req, res) {
passwordVerification(req, res);
});
}
function passwordVerification(req, res)
{
var userid = req.param('userid');
var password = req.param('password');
User.findOne({'username': userid},{'password': 1}, function(err, cb)
{
console.log(cb);
if(cb!= null)
{
if (password == cb.password) {
req.session.userid = userid;
res.render('index', { title: app.get('title'), 'userid': userid });
} else {
res.render('login', { title: app.get('title'), error: 'Invalid login'});
}
}
else
{
res.render('login', { title: app.get('title'), error: 'Invalid login'});
}
});
}
When I only have the "User Schema" in my schema.js, the database call from method "passwordVerification()" from index.js will return me the relevant password that was retrieve from the database. However, when I start adding in other schema such as "Question Schema" in schema.js, the method "passwordVerification()" will always return null.
When exporting multiple models from a single file like you are in schema.js, you need to give each exported model its own exports field name.
For example, replace the multiple module.exports = ... lines in schema.js with this code at the end of the file that exports all models:
module.exports = {
User: User,
Question: Question,
Answer: Answer,
Module: Module,
Role: Role
};
And then in index.js you can access the models like so:
var models = require('./schema');
...
models.User.findOne(...

Resources