how to get comments with their author with mongoose, nodejs - node.js

i am creating items inside collections and in each items authors are able to leave a comment. i want retrieve comment with their author . so that i referenced author id inside comment schema and i am only getting author id when i populate author in my get request. So can anyone help to get comments with its author information?
ITEM SCHEMA
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const itemSchema = new Schema(
{
name: { type: String },
comments: [
{
owner: { type: Schema.Types.ObjectId, ref: "User" },
text: { type: String },
},
],
owner: { type: Schema.Types.ObjectId, ref: "User" },
collections: { type: Schema.Types.ObjectId, ref: "Collection" },
},
);
itemSchema.index({ "$**": "text" });
export default model("Item", itemSchema);
GET COMMENT ROUTE
itemRouter.get(
"/:itemId/comments",
JWTAuthMiddleware,
adminAndUserOnly,
async (req, res, next) => {
try {
if (req.params.itemId.length !== 24)
return next(createHttpError(400, "Invalid ID"));
const items = await ItemModal.findById(req.params.itemId).populate("owner");
if (!items)
return next(
createHttpError(
400,
`The id ${req.params.itemId} does not match any items`
)
);
res.status(200).send(items.comments);
} catch (error) {
next(error);
}
}
);
What i am getting is only user id and comment

Related

Fulltext search engine with mongoose and node js not working properly

I am implementing full text search engine in my personal collection app in which there users, collections and items. When user searches , if even it is a comment inside item , it should be displayed. So i tried with text string of mongoose but it is working only with seperate schemas . Ex: only in collection schema or in items schema. I want to create only one route in which i should be able to search everything inside items and collection, even comments inside items. Here is what i have for now but it is giving me only collections property not referecned items schema details:
Collection schema
const collectionSchema = new Schema(
{
name: { type: String },
description: { type: String },
topic: { type: String },
image: { type: String },
customFields: [{ type: Object }],
owner: { type: Schema.Types.ObjectId, ref: "User" },
items: [{ type: Schema.Types.ObjectId, require: true, ref: "Item" }],
},
{ timestamps: true }
);
Collection Route
collectionRoute.get("/search", async (req, res, next) => {
try {
const { title } = req.query;
const searchitem = await CollectionModal.find({
$text: { $search: title },
});
res.status(200).send(searchitem);
} catch (error) {
next(error);
}
});

Mongoose - Selecting and Sending Virtuals from Populated Fields

I am having a little trouble getting my mongoose virtuals to show up from deep populated fields. Here is the code of the backend function that is not behaving as I'd like it to:
exports.get_user_feed = async (req, res, next) => {
const options = { sort: { date: -1 } };
const user = await User.find(
{ username: req.params.user },
"username posts avatar followers following"
)
.populate({
path: "posts",
options,
populate: [
{
path: "author",
},
{ path: "comments", populate: { path: "author" } },
],
})
.sort({ "posts.date": 1 });
res.json({ ...user });
};
And here is the comment schema:
const mongoose = require("mongoose");
const { DateTime } = require("luxon");
const Schema = mongoose.Schema;
const CommentSchema = new Schema({
targetPost: { type: Schema.Types.ObjectId, ref: "Post", required: true },
author: { type: Schema.Types.ObjectId, ref: "User", required: true },
date: { type: Date, required: true },
content: { type: String, maxlength: 400 },
comments: [{ type: Schema.Types.ObjectId, ref: "Comment" }],
stars: [{ type: Schema.Types.ObjectId, ref: "User" }],
});
// Virtual for post's URL
CommentSchema.virtual("url").get(function () {
return "/" + this.targetPost.url + this._id;
});
// Virtual for formatted date.
CommentSchema.virtual("formatted_date").get(function () {
return (
DateTime.fromJSDate(this.date).toLocaleString(DateTime.DATE_MED) +
" at " +
DateTime.fromJSDate(this.date).toLocaleString(DateTime.TIME_SIMPLE)
);
});
//Export model
module.exports = mongoose.model("Comment", CommentSchema);
My goal is to get the comments from each post to also include the formatted_date of the comment, but this virtual is not getting included in the response that is sent - all the regular properties are being sent but not the virtual. Any help here would be appreciated.
Add this code in your Commnet Schema file before module.exports.
CommentSchema.method('toJSON', function () {
const {
...object
} = this.toObject({ virtuals:true });
return object;
});

How can I delete referenced object id in mongodb in my node.js application?

I have been on this for days. I have tried so many things. I know I am not doing it rightly. I have a blog application I am building using node.js, mongodb and react.js. So, I have User Model, Post Model and Comment Model so far. I linked them up. What I did for post and comment models is this: When a user who made a comment deletes the comment, I want the referenced id of that comment in Post collection to be deleted as well from the database. It makes no sense if the comment referenced id remains in the database even though it has no content in it. It can be messy when you have many deleted comments. Comment Model is referenced in Post model. So, when a comment is deleted in Comment collection, the id referenced in the Post collection should be deleted as well. Look at my codes so far:
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',
},
postId:{
type: Schema.Types.ObjectId,
ref: "Post",
}
}, {timestamps: true}
);
CommentSchema.pre('update',function(next) {
this.model('Post').update(
{ },
{ "$pull": { "comments": this._id } },
{ "multi": true },
next
);
})
//exporting this schema
module.exports = mongoose.model("Comment", CommentSchema); //the module name is "Post"
I saw a post on this site about doing this with Mongodb middleware. I applied the middleware code inside my Comment Model as you can see above. It is not working. After deleting a comment, the referenced id in the Post collection is still there in the array. I am sure I am not doing it rightly.
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 deleting code
router.delete("/posts/:id/comment/:id", async (req, res) =>{
try{
const comment = await Comment.findById(req.params.id);
if(comment.author == req.body.author){
try{
await comment.delete()
res.status(200).json("Comment has been deleted")
}catch(err){
console.log(err)
}
}
else{
res.status(401).json("you can only delete your posts")
}
}catch(err){
console.log(err)
}
})
fetching post codes
//Get Post
router.get("/:id", async(req, res)=>{
try{
const post = await Post.findById(req.params.id).populate('username').populate({
path: "comments",
populate: {
path: "author",
}
})
This is a screenshot of the referenced id I am talking about. This particular comment has been deleted but the id is still found in that particular post array where the comment was made.
Your code would be somewhat like this:
deleteCommentById: async function (req, res) {
try {
if (req.params.type === "Comment") {
await postModel.updateMany({ postId: ObjectId(req.params.postId), type: 'Post' }, { $pull: { 'children': ObjectId(req.params.id) } });
await commentModel.updateMany({ postId: ObjectId(req.params.postId) }, { $pull: { 'comments': ObjectId(req.params.id) } });
}
await postModel.findByIdAndDelete(ObjectId(req.params.id));
return res.status(http_status.OK).json({ message: `${req.params.type} deleted successfully` });
} catch (error) {
return res.send("error");
}
}
SO, your code too will be somewhat like the code above which I have written as reference for you.
So instead of this :
CommentSchema.pre('update',function(next) {
Try to use .pre('remove') or .pre('delete') instead of update

How to delete comment that is nested in Post schema with mongoose and nodejs?

I want to be able to delete comment that is inside my Post model.
This is my Schema for Post model:
const PostSchema = new Schema({
userID: {
type: Schema.Types.ObjectId,
ref: 'user'
},
content: {
type: String,
required: true
},
registration_date: {
type: Date,
default: Date.now
},
likes: [
{
type: Schema.Types.ObjectId,
ref: "user"
}
],
comments: [
{
text: String,
userID: {
type: Schema.Types.ObjectId,
ref: 'user'
}
}
]
})
And I have this route:
router.delete('/comment/:id/:comment_id', auth, async (req, res) => {
const postId = req.params.id
const commentId = req.params.comment_id
}
comments in post looks like this:
comments: [
{
_id: 5f1df4cf5fd7d83ec0a8afd8,
text: 'comment 1',
userID: 5efb2296ca33ba3d981398ff
},
{
_id: 5f1df4d35fd7d83ec0a8afd9,
text: 'commnet 2',
userID: 5efb2296ca33ba3d981398ff
}
]
I want to delete comment, and don't know how to do it. Does anyone have idea how to do it?
First we find the post by findByIdAndUpdate then we delete the comment using $pull from the array of comments.
router.delete("/comment/:id/:comment_/id", async function (req, res) {
try {
const post = await Post.findByIdAndUpdate(
req.params.id,
{
$pull: { comments: {_id:req.params.comment_id}},
},
{ new: true }
);
if (!post) {
return res.status(400).send("Post not found");
}
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});

Is it possible to query subdocuments directly using mongoose?

let's say there was a User model and a Post model. In this situation User's would have many posts; User would be the parent and Post would be the child. Is it possible to query for posts directly?
For instance if I wanted to do something like
app.get('/post/search/:query', (req,res) => {
Posts.find({title: req.params.query }, (err,post) => {
res.send(JSON.stringify(post))
})
})
or would one have to do:
app.get('/post/search/:query',(req,res) => {
let resultsFromQuery = [];
User.find({'post.title':req.params.query'}, (err,user) => {
user.posts.forEach((post) => {
if(post.title === req.params.query){
resultsFromQuery.push(post);
}
})
})
res.send(JSON.stringify(resultsFromQuery))
})
EDIT: Here is my schema's.
User Schema (Parent)
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
PostSchema = require('./post.js');
let UserSchema = new Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
posts: [PostSchema]
})
module.exports = mongoose.model('User',UserSchema);
Post Schema (Child)
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
let PostSchema = new Schema({
title: {
type: String
},
description: {
type: String
},
image: {
type: String
},
original_poster: {
id: {
type: String,
required: true
},
username: {
type: String,
required: true
}
},
tags: {
type: [String],
required: true
}
})
module.exports = PostSchema;
EDIT:
Here is a sample document
the result of db.users.find({username: 'john'})
{
"_id" : ObjectId("5a163317bf92864245250cf4"),
"username" : "john",
"password" : "$2a$10$mvE.UNgvBZgOURAv28xyA.UdlJi4Zj9IX.OIiOCdp/HC.Cpkuq.ru",
"posts" : [
{
"_id" : ObjectId("5a17c32d54d6ef4987ea275b"),
"title" : "Dogs are cool",
"description" : "I like huskies",
"image" : "https://media1.giphy.com/media/EvRj5lfd8ctUY/giphy.gif",
"original_poster" : {
"id" : "5a163317bf92864245250cf4",
"username" : "john"
},
"tags" : [
"puppies",
"dogs"
]
}
],
"__v" : 1
}
Yes you can find directly the post title from the user model. like bellow
User.find({"posts.title": "Cats are cool"}, (err, users) => {
if(err) {
// return error
}
return res.send(users)
})
That will return user with all post not only the matching post title. So to return only matching post title can use $ positional operator. like this query
User.find({"posts.title": "Cats are cool"},
{username: 1, "posts.$": 1}, // add that you need to project
(err, users) => {
if(err) {
// return error
}
return res.send(users)
})
that only return matching post
Since you are saving OP data, why not do:
// you'll need to adapt how your are getting the user-id here
const { user } = req
Post.find({ title: 'the title', 'original_poster.id': user.id }, (err, posts) => {
console.log(posts); })
Though I would advise you to adjust your Post-schema:
original_poster: {
type: Schema.Types.ObjectId,
ref: 'User'
}
},
Then you can do Post.find({}).populate('original_poster') to include it in your results.!

Resources