skip to Main Content

There’re hundreds of items and each item contains comments. Each comment contains author, content, likes, replies. Since each item is likely to have hundreds of comments, I embedded comment’s id into sub-array inside item document.

const itemSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: true,
      trim: true,
      index: true,
    },
    author: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "User",
      required: true,
    },
    comments: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],    
  },
  { timestamps: true }
);

const commentSchema = new mongoose.Schema(
  {
    author: {
      type: mongoose.Schema.Types.ObjectId,
      ref: User,
      required: true,
    },
    content: {
      type: String,
      required: true,
      trim: true,
    },
    likes: {
      type: [{ type: mongoose.Schema.Types.ObjectId, ref: User }],
      default: Array,
    },
    replies: { type: [this], default: Array },
  },
  { timestamps: true }
);

When I fetch an item, I am going to load 10 latest comments for that item. On the client side, there is load more button. Every time a user clicks on it, it loads 10 more previous comments. How can I achieve this with mongoose in Express.js?

2

Answers


  1. const pageSize = 10; // Number of comments per page
    const pageNumber = 1; // Page number (adjust as needed)
    
    const pipeline = [
      // Match the relevant post
      { $match: { _id: ObjectId("your_post_id") } },
    
      // Project to retrieve only the comments array
      { $project: { _id: 0, comments: 1 } },
    
      // Unwind the comments array to prepare for sorting
      { $unwind: "$comments" },
    
      // Sort the comments by creation date in descending order
      { $sort: { "comments.created_at": -1 } },
    
      // Skip to the appropriate page
      { $skip: (pageNumber - 1) * pageSize },
    
      // Limit the number of comments per page
      { $limit: pageSize },
    
      // Group the comments back into an array
      { $group: { _id: null, comments: { $push: "$comments" } } }
    ];
    
    const result = await db.posts.aggregate(pipeline).toArray();
    
    // Extract the comments array from the result
    const sortedComments = result.length > 0 ? result[0].comments : [];
    

    You can skip sort pipeline if createdAt is not present in comment object.

    Login or Signup to reply.
  2. If the comments are sorted, you can simply use $slice:

    const result = await posts.find(
      { _id: ObjectId("your_post_id")},
      { comments: { $slice: [ pageNumber * pageSize , pageSize  ] } } 
    ).populate('comments').exec();
    

    Where pageSize = 10 in your case.

    If the comments are not sorted, you can sort them using a pipeline with $sortArray. With current mongoDB versions there is no reason to $unwind and $group again.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search