0

const userSchema = new Schema({
    username: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    posts: [{
        type: Schema.Types.ObjectId,
        ref: 'Post'
    }],
    friends: [{
        type: Schema.Types.ObjectId,
        ref: 'User'
    }],
});

// Exporting the schema so it can be accessed by requiring it.
module.exports = mongoose.model('User', userSchema);

As you can see I got this user schema that has a friends array and a posts array.

User.findById(userId).then(result => {
        Post.find(query).then(posts => {
            res.status(200).json(posts)
        }).catch(err => {
            if (!err.statusCode) {
                err.statusCode = 500;
            }
            next(err);
        })
    });

Is there any query that can fit in the find() above in order to get all the posts of the user's friends?

1
  • could you add the Post schema to the question? Commented May 7, 2020 at 23:03

2 Answers 2

1

If in the post model you have a link to the user model, that is, some field that identifies who wrote the post, you could use a for loop to search for posts made by the user's friends.

I don't know if this is the best solution but I hope it helps.

As a tip, you should use asynchronous syntax instead of promises, this helps when correcting errors.

async function getFriendsPosts(req,res){
  /*in this array we will store the 
    posts of the user's friends */
  let posts = [];
  
    try{
          //we check if the user exists
          let user = User.findById(req.params.id);
          //if it doesn't exist we will send a message
          if(!user) res.status(404).send("User not Found");
          else{
              /* here we compare the id of the friends with the id of
               the friends with the "creator" field in the post model*/
              for await(let friend of user.friends){
                for await(let creator of Post.find()){
                    /* if there is a match we send 
                       it to the post array*/
                    if(friend._id.equals(creator._id)){
                      posts.push(creator);
                    }
                }
              }
              
              /*finally we send the array with the posts*/
              res.send(posts);
          }
    
    
    }catch(err){
      res.status(500).send("Internal Server Error");
    }


}

Sign up to request clarification or add additional context in comments.

Comments

0

If I suppose that the Post Schema is something like that

{
    title: String,
    content: String,
    owner: { type: Schema.Types.ObjectId, ref: 'User'}
}

then we can use aggregate pipeline to get the friends posts of some user

something like that

db.users.aggregate([
  {
    $match: {
      _id: "userId1" // this should be of type ObjectId, you need to convert req.params.id to ObjectId (something like: mongoose.Types.ObjectId(req.params.id) instead of 'userId1')
    }
  },
  {
    $lookup: {
      from: "posts",
      let: {
        friendsIDs: "$friends"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: ["$owner", "$$friendsIDs"]
            }
          }
        }
      ],
      as: "friendsPosts"
    }
  }
])

you can test it here Mongo Playground

feel free to replace these 'userId1', 'userId2', ..., 'postId1, 'postId2', .. in this link with your real users and posts Ids

by this way, you got the friends posts of some user in one query rather than two queries

then the function will be something like that

User.aggregate([
  {
    $match: {
      _id: mongoose.Types.ObjectId(req.params.id)
    }
  },
  {
    $lookup: {
      from: "posts", // this should be the posts collection name, It may be 'Post' not 'posts', check it
      let: {
        friendsIDs: "$friends"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: ["$owner", "$$friendsIDs"]
            }
          }
        }
      ],
      as: "friendsPosts"
    }
  }
]).then(result => {
    // the aggregate pipeline is returning an array
    // but we are sure it will be an array of only one element as we are searching for only one user, so we can use result[0]

    result = result || []; // double check the result array
    result[0] = result[0] || {}; // double check the user object
    var posts = result[0].friendsPosts; // here is the friends posts array

    // return the posts array
    res.json(posts);
})

hope it helps


Update

If we need to sort the firendsPosts, and then limit them

we can use the following

db.users.aggregate([
  {
    $match: {
      _id: "userId1"
    }
  },
  {
    $lookup: {
      from: "posts",
      let: {
        friendsIDs: "$friends"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$owner",
                "$$friendsIDs"
              ]
            }
          }
        }
      ],
      as: "friendsPosts"
    }
  },
  {
    $unwind: "$friendsPosts" // unwind the array to get a stream of documents
  },
  {
    $sort: {
      "friendsPosts.createdAt": 1 // then sort the posts by the createdAt Date in ascending order
    }
  },
  {
    $group: { // then group the posts again after sorting
      _id: "$_id",
      friendsPosts: {
        $push: "$friendsPosts"
      }
    }
  },
  {
    $project: { 
      friendsPosts: {
        $slice: ["$friendsPosts", 2] // this is to limit the posts
      }
    }
  }
])

you can test it here Mongo Playground 2

4 Comments

Is there a way to perform limit and sort on the friendsPosts
yes, there is, I've updated my answer, could you check it again to see the last update?
Great! Just one more thing, is this possible to say like I want to get only the posts that were created after a specific date?
we can use another $match stage after the $sort and before $group, to get the posts that have been created after some date, try this mongoplayground.net/p/rdP4ZmiMJdk

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.