I'm trying to populate post with comments. With populating author I didn't have any problems. I tried to populate just comments without author and it didn't work..
Here is the comment model:
const mongoose = require('mongoose');
const schema = mongoose.Schema;
const User = require('./user');
commentSchema = new schema({
comment: String,
author: { type: schema.Types.ObjectId, ref: 'User' },
})
const Comment = module.exports = mongoose.model('Comment', commentSchema);
Here is the route:
router.get('/posts/:id', (req, res) => {
Post.findById({ _id: req.params.id })
.populate('author')
.populate('comments')
.exec((err, post) => {
if (err) {
console.log(err);
} else {
res.json(post);
}
});
});
Related
hey i am new here in nodejs and mongodb, i tyied to push comments on post in my social media project..
Here is my controller ,it shows error while pushing comments in mongodb
TypeError: Cannot read property 'push' of undefined
const Comment = require('../models/comment')
const Post = require('../models/post')
module.exports.create = function(req,res){
Post.findById(req.body.post, function(err ,post){
if(post){
Comment.create({
content: req.body.content,
post: req.body.post,
user: req.body._id
},function(err, comment){
if(err){console.log("error in pushing comment")}
post.comments.push(comment),
post.save()
res.redirect('/')
})
}
})
}
this is my comments schema
const mongoose = require('mongoose')
const commentSchema = new mongoose.Schema({
content: {
type: String,
required: true
},
//comments belongs to user
user : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
post : {
type: mongoose.Schema.Types.ObjectId,
ref : 'Post'
}
},{
timestamps: true
})
const Comment = mongoose.model('Comment' , commentSchema)
module.exports = Comment
I recommend that you use Promises instead of callback functions. It will make your code way more readable. Monogoose findOneAndUpdate could be handy here.
As for the error, you should make a console.log(post.comments) to see the value for yourself.
We should see the model of Post. It should contain an array of Comments
const mongoose = require('mongoose')
const Comment = require('./comment.model.js') // Change the path
const postSchema = new mongoose.Schema({
// comments belongs to post
comments : {
type: [Comment]
},
// Other attributes here
},{
timestamps: true
})
const Post = mongoose.model('Post' , postSchema)
module.exports = Post
You'll end up with something like this:
const Comment = require('../models/comment')
const Post = require('../models/post')
module.exports.create = function (req, res) {
Post.findById(req.body.post).then((post) => {
if (post) {
Comment.create({
content: req.body.content,
post: req.body.post,
user: req.body._id
}).then((comment) => {
Post.findOneAndUpdate(
{ _id: req.body.post },
{ $push: { comments: comment } }
).then(() => {
res.redirect('/')
}).catch((error) => console.log(error))
})
}
}).catch((error) => console.log(error))
}
SEE EDIT AT BOTTOM OF QUESTION.
I have a Node.js Express web application using MongoDB and Mongoose with collections for articles and comments. They have a one-to-many association where one article can have many comments.
The mongoose model schema is as follows:
// models/article
const mongoose = require('mongoose');
const articleSchema = new mongoose.Schema({
title: { type: String },
content: { type: String },
}, {timestamps: true});
module.exports = mongoose.model('Article', articleSchema);
and
// models/comment.js
const mongoose = require('mongoose');
const commentSchema = new mongoose.Schema({
content: { type: String },
article: { type: mongoose.Schema.Types.ObjectId, ref: 'Article' },
}, {timestamps: true});
module.exports = mongoose.model('Comment', commentSchema);
I have a route with a parameter for the article id
// routes.js
router.get('/articles/:articleId/comments', commentsController.list);
And a controller with a callback function to query the database and return the comments with the given article id. It uses the mongoose find() method filtering on the article id taken from the route parameter.
// controllers/commentsController.js
exports.list = (req, res, next) => {
Comment.find({ article: req.params.articleId })
.exec((err, comments) => {
res.render('comments/list', { title: 'Comments', comments: comments });
});
};
But this turns up no results. Just experimenting I can see that the req.params.articleId is a string and any comment.article is an object so they match with a loose comparison == but not a strict comparison === unless I convert comment.article.toString(). Anyway, what is the proper way to do such a query. All my attempts have failed.
EDIT: I found the problem. The code above is as it should be. The issue must be related to how I seeded the DB which I did directly in MongoDB. I deleted all those records and just added them from the application and it works with the code above.
One way to approach this is to add the comments to your article model.
const mongoose = require('mongoose');
const articleSchema = new mongoose.Schema({
title: { type: String },
content: { type: String },
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
}, {timestamps: true});
articleSchema.set('toJSON', {
transform: (document, returnedObject) => {
const article = returnedObject
article.id = article._id.toString()
delete article._id
}
})
module.exports = mongoose.model('Article', articleSchema);
Then get the comments in one of these ways:
const router = require('express').Router()
const Article = require('../models/article')
const Comment = require('../models/comment')
// article with comments
router.get('/:id', async (request, response, next) => {
try {
const article = await Article.findById(request.params.id)
.populate(
'comments', {
content: 1
}
)
response.json(article.toJSON())
} catch (err) {
console.log(err)
}
})
// list of comments belonging to an article
router.get('/:id/comments', async (request, response, next) => {
try {
const article = await Article.findById(request.params.id)
if (!article) {
response.status(404).json({ error: 'invalid request' })
}
const comments = await Comment.find({ article: request.params.id })
.populate(
'article', {
title: 1
}
)
response.json(comments.map(comment => comment.toJSON()))
} catch (err) {
console.log(err)
}
})
module.exports = router
Client.js
const mongoose = require("mongoose");
var Schema = mongoose.Schema;
const clientSchema = new mongoose.Schema(
{
name: { type: String, required: true, default: "" },
}, {
timestamps: true
}
);
module.exports = mongoose.model("Client", clientSchema);
User.js
const mongoose = require("mongoose");
var Schema = mongoose.Schema;
const userSchema = new mongoose.Schema({
name: { type: String, required: true, default: "" },
clients: [{
client: {
type: Schema.Types.ObjectId,
ref: "Client",
default: null
},
user_group: {
type: Number
default: null
}
}]
}, { timestamps: true });
module.exports = mongoose.model("User", userSchema);
auth.js (Where trying to populate Clients)
const express = require("express");
const router = express.Router();
const User = require("../models/User");
const Client = require("../models/Client");
router.post("/users", (req, res) => {
let params = req.body;
let total_client = [];
User.findOne({
email: params.email
})
.populate({
path: "clients.client",
model: Client
})
.exec((err, user) => {
console.log(user);
res.send(user);
});
});
module.exports = router;
Please check the above code. I have given code examples of my two models user.js and client.js. In user schema, I have referenced client inside an array object. While querying user, the client is not population. Please help me to get this thing done. Thanks in advance.
The following expects you to provide a name in the json body of your post request (your example uses email which does not exist in the user model). Also, your model is already defining the ref: Client and so you can simplify your request to just include the path clients.client.
router.post("/users", async (req, res) => {
const { name } = req.body;
const user = await User.findOne({ name: name }).populate('clients.client').exec();
res.send(user);
});
Solved this problem just adding an extra parameter in module export of client.js file
module.exports = mongoose.model("Client", clientSchema, "client");
I have a node-express application that connects to MongoDB using the Mongoose library.
But I'm having problems getting my custom Mongoose plugins to bring changes to the documents before they are saved to the database.
Here is how I define my plugin:
const requestContext = require('request-context');
module.exports = (schema, options) => {
schema.pre('save', next => {
const author = requestContext.get('request').author;
this._createdBy = author.sub;
this._owner = author.sub;
this._groupOwner = author.group;
next();
});
schema.pre('findOneAndUpdate', next => {
const author = requestContext.get('request').author;
this._lastEditAt = Date.now();
this._lastEditBy = author.sub;
next();
});
}
Then I add it to the schema like this:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const trace = require('../plugins/trace');
const PostSchema = new Schema({
title: String,
Text: String,
category: String,
_createdAt: {
type: Date,
default: Date.now
},
_lastEditAt: Date,
_createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
_lastEditBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
_owner: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},_groupOwner: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Group'
}
});
PostSchema.plugin(trace);
exports.schema = PostSchema;
exports.model = mongoose.model('Post', PostSchema);
In my Express controller:
const router = require('express').Router();
const Post = require('../model/post').model;
router.post('/', (req, res) => {
const post = new Post(req.body);
post.save()
.then(() => res.json(post))
.catch(err => res.status(400).json(err));
});
router.put('/', (req, res) => {
Post.findByIdAndUpdate(req.body._id, req.body, {new: true})
.then(post => res.json(post))
.catch(err => res.status(400).json(err));
});
The pre hooks defined by the plugin are triggered but the changes they bring are never persisted to the database. Is this a bug in Mongoose plugin system.
I have tried with Mongoose#4.13.9 and Mongoose#5.3.3 but none works.
I was struggling with this issue during all the week-end.
Luckily I have found the origin of the problem.
First: I was using arrow functions for my hook methods, which changes the context of the this keyword.
So I had to define my hook functions using the old es5 function syntax as follows:
const requestContext = require('request-context');
module.exports = (schema, options) => {
schema.pre('save', function(next) {
const author = requestContext.get('request').author;
this._createdBy = author.sub;
this._owner = author.sub;
this._groupOwner = author.group;
next();
});
}
I am trying to make some nested population. User model is populated the way i expect it to be.
But i am having trouble with nested population of a Post model. It's not populated at all.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
mongoose.connect(`mongodb://localhost:27017/testDB`);
var UserSchema = new Schema({
name:String,
post: {
type: Schema.Types.ObjectId,
ref: 'Post'
}
});
var PostSchema = new Schema({
title:String,
subscriber:{
type:Schema.Types.ObjectId,
ref:'Subscriber'
}
});
var SubscriberSchema = new Schema({
name:String
});
var User = mongoose.model("User", UserSchema);
var Post = mongoose.model('Post',PostSchema);
var Subscriber = mongoose.model('Subscriber',SubscriberSchema);
User
.find()
.populate([{
path:'post',
model:'Post',
populate:{
model:'Subscriber',
path:'subscriber'
}
}])
.exec()
.then(function(data){
console.log(data);
mongoose.disconnect();
});
You are calling .populate incorrectly.
Please try this:
User
.find()
.populate('post')
.populate({
path: 'post',
populate: {
path: 'subscriber',
model: 'Subscriber'
}
})
.exec()
.then(function (data) {
console.log(data);
mongoose.disconnect();
});
For more info check this out.