2

I have a below mongoose schema:

const PostSchema=mongoose.Schema({
    author: String,
    uid: String,
    comments:[
        {
            commentAuthor: String,
            uid: String,
            comment: String,
            userProfile: String,
            dateOfComment: String,
            replies: [{
                replyAuthor: String,
                authorProfile: String,
                reply: String,
                dateOfReply: String
            }]
        }
    ]
},{collection: 'SocialPost'});

I have fetch all those posts in which a particular user with a given id has commented i.e. all those documents in which comments array contains an object in which uid is equal to the given uid. For ex. suppose the user id is 101, then the result of the query might look like:

[{
 author: 'gratis',
 uid: 101,
 ...,
 comments:[{
  commentAuthor: 'gratis',
  uid: 101,
  ...
},...]
},
...]

To put everything in perspective, I want to fetch all those posts on which a particular user has commented , or even replied, along with the comments and replies of only that user.

The best I could do is this:

SocialPost.aggregate([
{$match:{}},
{$unwind: "comments"},
{'$match': {"comments.uid": req.body.uid}}
])

This query is returning only the first matched item of comments array of respective post. I want all items that match the condition.

Please help me to design the right query for my problem.

Thank You!

6
  • Why my question is not receiving any response? Shall I edit it? Commented Jun 30, 2021 at 9:53
  • Which field in replies relates to your uid? Schema is not showing any uid. Commented Jun 30, 2021 at 9:59
  • It is the uid field Commented Jun 30, 2021 at 10:00
  • Apologies I meant replies. Your sample data shows data in uid but schema shows no field uid in replies. Commented Jun 30, 2021 at 10:01
  • Actually I haven't added the uid in replies. I can surely do it later, which is not a big task. Commented Jun 30, 2021 at 10:03

2 Answers 2

2

If you have to arrays and you want to find a doc which has a value in any on of them you might want to use $or along with $elemMatch.

SocialPost.find({
    "$or": [
         { 'comments': { "$elemMatch" : { 'uid' : yourId} } },
         {'comments.replies':{"$elemMatch":{'uid':yourId}}}
     ]
},(err,result)=>{});
Sign up to request clarification or add additional context in comments.

5 Comments

This might be the answer. But is there any way so that I get only the comments posted by the concerned user so that I don't have to filter it manually.
Hey, I am not that good in mongodb, but I have found this which looks a bit similar to my problem. Could you please take a look at it.
Yes I was thinking of something similar. I also do not know a lot, but with this query as your query stage, you can use aggregations to find only the matching array objects. One more link stackoverflow.com/questions/36229123/…
I think I should manually filter the array, no matter how much time it takes because using aggregate doesn't looks a good idea.
Yup. With multi level nesting that would be easier to achieve. I will upvote this question, hopefully you get a complete solution.
1

Although your approach is not incorrect per se, writing the comments as an array of objects inside the PostsSchema leads to the kind of problems that you're experiencing now, as running any kind of query involving comments from more than one post gets a little bit too verbose. Personally, my approach to this problem would be creating a CommentsSchema totally separated from the posts' and then link both via Mongoose's populate() method. The new schema could be something like this:

const CommentsSchema = new mongoose.Schema({
  userId: {
    type: Schema.Types.ObjectId,
    required: true,
    ref: Users
  },
  postId: {
    type: Schema.Types.ObjectId,
    required: true,
    ref: Posts
  },
  parentCommentId: {
    type: Schema.Types.ObjectId,
    ref: Comments
  },
  comment: String,
  timestamps: true
});

This way, each comment would include information about the user who created it (assuming you have a UsersSchema and a collection of Users) via userId, the post that it refers to (postId) and, if the comment is an answer to another one, the comment it answers (parentCommentId). In order to extract a post's comments as an extra field in your Posts collection, you could create a virtual field in your PostsSchema:

PostsSchema.virtual('comments', {
  ref: Comments,
  localField: '_id',
  foreignField: 'postId'
});

Then, when you have a post document, you can populate its comments like this: post.populate("comments").execPopulate(); Finally, extracting all the comments written by a user would just be a matter of querying your Comments collection:

const userComments = await Comments.find({ userId: user._id });

I know these are a lot of changes. But hopefully this approach will be much easier to deal with.

1 Comment

Thanks for the answer. The approach you have mentioned is clearly the favourable one. But I have proceeded in my project to such an extent that I cannot do such a major change. Thats why I was looking for an answer in my appraoch

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.