Stijnk008
Stijnk008

Reputation: 232

Get mutual followers in mongoose

I want to get mutual (so from all users the user I have the most shared followers with (we both follow the same user) sorted by that count). followers from my database scheme (mongoDB) with an aggregation function.

user scheme:

let User = new mongoose.Schema({
uid: {
    type: String,
    require: true,
    unique: true
},
    followers: [String],
    following: [String]
})

where followers/following is an array of UID's

I've tried this:

let me = await User.findOne({uid: uid})
if (me) {
    console.log(me.followers)
    let mutual = await User.aggregate([
        // Match documents that contain the elements
        { "$match": { 
            "followers": { "$in": me.followers }
        }},

        // // De-normalize the array field content
        { "$unwind": "$followers" },

        // Match just the elements you want
        { "$match": { 
            "followers": { "$in": me.followers }
        }},

        // Count by the element as a key
        { "$group": {
            "_id": "$name",  
            "mutual": { "$sum":  1}
        }}
    ])
    if (mutual) {
        console.log('mutual', mutual)
    }
}

but this doesn't give the correct count

Upvotes: 0

Views: 375

Answers (1)

Joe
Joe

Reputation: 28356

You can use $filter and $size instead of unwinding the followers for each person. You can also do this with an $lookup instead of 2 client-side queries.

The stages in the below pipeline:

  • $match the user in question
  • $lookup retrieves all uses that have a common follower, placing them in the matched array
  • $unwind the matched array to consider each user separately
  • $match - since the user in question would also be matched as having a matching follower, eliminate this 'self' entry
  • $project eliminates all of the fields except the other person's uid, and for commonFollowers:
    • $filter to iterate each persons followers array, keeping only those entries that match the user in question
    • $size to return the number of elements left
[
  {"$match": {"uid": uid}},
  {
    "$lookup": {
      "from": "User",
      "localField": "followers",
      "foreignField": "followers",
      "as": "matched"
  }},
  {"$unwind": "$matched"},
  {"$match": {"$expr": {"$ne": ["$matched.uid", "$uid"]}}},
  {"$project": {
      "_id": 0,
      "uid": "$matched.uid",
      "commonFollowers": {
        "$size": {
          "$filter": {
            "input": "$matched.followers",
            "cond": {
              "$in": [
                "$$this",
                "$followers"
              ]
  }}}}}},
  {"$sort": {"commonFollowers": -1}}
])

Upvotes: 1

Related Questions