0

I am using Nodejs and MongoDB, mongoose and expressjs, creating a Blog API having users, articles, likes & comments schema. Below are schemas that I use.


    const UsersSchema = new mongoose.Schema({
      username:        { type: String },
      email:           { type: String },
      date_created:    { type: Date },
      last_modified:   { type: Date }
    });


    const ArticleSchema = new mongoose.Schema({
      id:              { type: String, required: true },
      text:            { type: String, required: true }, 
      posted_by:       { type: Schema.Types.ObjectId, ref: 'User', required: true },
      images:          [{ type: String }],
      date_created:    { type: Date },
      last_modified:   { type: Date }
    });


    const CommentSchema = new mongoose.Schema({
      id:             { type: String, required: true },
      commented_by:   { type: Schema.Types.ObjectId, ref: 'User', required: true },
      article:        { type: Schema.Types.ObjectId, ref: 'Article' },
      text:           { type: String, required: true },
      date_created:   { type: Date },
      last_modified:  { type: Date } 
    });

What I actually need is when I * get collection of articles * I also want to get the number of comments together for each articles. How do I query mongo?

byal
  • 167
  • 1
  • 12

1 Answers1

1

Since you need to query more than one collection, you can use MongoDB's aggregation.

Here: https://docs.mongodb.com/manual/aggregation/

Example:

Article
  .aggregate(
    {
      $lookup: {
        from: '<your comments collection name',
        localField: '_id',
        foreignField: 'article',
        as: 'comments'
      }
    },
    {
      $project: {
        comments: '$comments.commented_by',
        text: 1,
        posted_by: 1,
        images: 1,
        date_created: 1,
        last_modified: 1
      }
    },
    {
      $project: {
        hasCommented: {
          $cond: {
            if: { $in: [ '$comments', '<user object id>' ] },
            then: true,
            else: false
          }
        },
        commentsCount: { $size: '$comments' },
        text: 1,
        posted_by: 1,
        images: 1,
        date_created: 1,
        last_modified: 1
      }
    }
  )

The aggregation got a little big but let me try to explain: First we need to filter the comments after the $lookup. So we $unwind them, making each article contain just one comment object, so we can filter using $match(that's the filter stage, it works just as the <Model>.find(). After filtering the desired's user comments, we $group everything again, $sum: 1 for each comment, using as the grouper _id, the article's _id. And we get the $first result for $text, $images and etc. Later, we $project everything, but now we add hasCommented with a $cond, simply doing: if the $comments is greater than 0(the user has commented, so this will be true, else, false.

MongoDB's Aggregation framework it's awesome and you can do almost whatever you want with your data using it. But be aware that somethings may cost more than others, always read the reference.

  • It helps a lot. Thanks! – byal Jan 23 '19 at 09:57
  • How can I find if a user commented on the article. I want to add 'haveCommented' field on each of article collection list – byal Jan 23 '19 at 11:21
  • Sorry, I didn't understand. You want to add `haveCommented` property on the article's object? Because for what I'm seeing from your schemas, many users may comment on an article, so this would be an array of users' _id on the article's object? – Matheus Hernandes Jan 23 '19 at 13:57
  • Sorry for my poor english but, what i actually mean on the previous comment was, I want to check if the user commented on the article and return 'haveCommented': true / false Boolean field inside the articles collection. How can i do that? – byal Jan 23 '19 at 13:58
  • You can post a final object that you want to achieve on the question, I guess that will help me help you better. – Matheus Hernandes Jan 23 '19 at 13:58
  • Ok, here is the final object that I want to achieve. { "images": String, "text": "String", "posted_by": userId, "comments_no": Number, "likes_no": Number, "haveCommented", Boolean } – byal Jan 23 '19 at 14:00
  • You don't need to be sorry for that. I assume that isn't your native idiom, so you're having an extra effort here. You've a single user id and you want to know if this user has commented on X articles? That's it? – Matheus Hernandes Jan 23 '19 at 14:00
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/187190/discussion-between-matheus-hernandes-and-byal). – Matheus Hernandes Jan 23 '19 at 14:01