node.jsmongodbmongooseaggregates

Get all articles by page size of 10 and each article get populated some needed users' information


I have created 2 models namely user, article. Their data structure are like below;

const userSchema = Schema({
  name: String,
  email: String,
  password: String
});
const User = mongoose.model(‘user’, userSchema);
module.exports = User;

——

const articleSchema = Schema({
  liked: Array,  // liked contains users’ _id who like this article
  content: String
})
const Article = mongoose.model(‘article’, articleSchema);
module.exports = Article

——

I want to get all articles by page size of 10 and each article get populated some users’ info who like this article.

For example, let’s imagine the data of user table and article table like this:

users = [
 { 
    _id: 1,
    name: ‘a’,
    email: ‘a.com’,
    password: ‘a’
 },
 { 
    _id: 2,
    name: ‘b’,
    email: ‘b.com’,
    password: ‘b’
 }
]

articles = [
  {
    _id: 1,
    liked: [1,2],
    content: “a&b”
  },
  {
     _id: 2,
     liked: [1],
     content: “a”
  }
]

The desired result is:

  articles = [
   {
      _id: 1,
      liked: [
      { 
         _id: 1,
         name: ‘a’,
         email: ‘a.com’,
         password: ‘a’
       },
       { 
         _id: 2,
         name: ‘b’,
         email: ‘b.com’,
         password: ‘b’
       }
     ],
     content: “a&b”
   },
   {
       _id: 2,
       liked: [
       { 
          _id: 1,
          name: ‘a’,
          email: ‘a.com’,
          password: ‘a’
        }
       ],
        content: “a”
    }
]

I tried to resolve this matter by using Model.find(query) and Model.aggregate(pipeline) but each of the two approaches had pros and cons and couldn’t reached the good result. Please give me an advice.


Solution

  • Based on what you have shared a simple $lookup in an Model.aggregate() query would get you that output:

    const articles = await Article.aggregate([
      {
        "$lookup": {
          "from": "users",
          "localField": "liked",
          "foreignField": "_id",
          "as": "liked"
        }
      }
    ]);
    

    See HERE for a working example.

    However, if you were to change your schema to use referenced docs then you could use populate to replace each _id in the Article.liked array with the corresponding User document at the time of query retrieval. Either way will work.

    This is how you would use populate:

    const articleSchema = Schema({
      liked: [{ 
          type: mongoose.Schema.Types.ObjectId, 
          ref: 'User' // create a reference to User
       }],  
      content: String
    })
    

    then do

    const articles = await Article.find().populate({path: 'liked', model: User});
    

    Note: this example with populate() works on the predicate that each User._id field of each User document is of the mongoose ObjectId type.