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!

Gratis Devs
  • 155
  • 9

2 Answers2

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)=>{});
Tushar Shahi
  • 16,452
  • 1
  • 18
  • 39
  • 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. – Gratis Devs Jun 30 '21 at 10:06
  • Hey, I am not that good in mongodb, but I have found [this](https://stackoverflow.com/a/42299322/14552734) which looks a bit similar to my problem. Could you please take a look at it. – Gratis Devs Jun 30 '21 at 10:10
  • 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 https://stackoverflow.com/questions/36229123/return-only-matched-sub-document-elements-within-a-nested-array – Tushar Shahi Jun 30 '21 at 10:14
  • I think I should manually filter the array, no matter how much time it takes because using aggregate doesn't looks a good idea. – Gratis Devs Jun 30 '21 at 10:19
  • Yup. With multi level nesting that would be easier to achieve. I will upvote this question, hopefully you get a complete solution. – Tushar Shahi Jun 30 '21 at 10:25
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.

amda
  • 365
  • 2
  • 10
  • 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 – Gratis Devs Jun 30 '21 at 10:02