code.mevn
code.mevn

Reputation: 165

How to aggregate two nested arrays for two different outputs of single collection?

My desired outcome is check common ids and their count between friends. One is closed friends count and another one is normal friends count.

Here data is the list of my friends and user is me.

async function uinfo(data, user){
  const arr = []
    for (const i of data) {
      await Profile.aggregate([{"$match": {"_id": {"$in": [user, i._id]}}}, {"$unwind": "$list.closedFriends"}, {"$group": {_id: "$list.closedFriends.id", count: {$sum: 1}}}, {$match: {count: 2}}, {$project: {_id: 1}}], function(err, result){
          if(result.length >= 1){
            i.cfcount = result.length
          }
          else{
            i.cfcount = 0
          }
      })
      await Profile.aggregate([{"$match": {"_id": {"$in": [user, i._id]}}}, {"$unwind": "$list.friends"}, {"$group": {_id: "$list.friends.id", count: {$sum: 1}}}, {$match: {count: 2}}, {$project: {_id: 1}}], function(err, result1){
          if(result1.length >= 1){
            i.fcount = result1.length
          }
          else{
            i.fcount = 0
          }
      })
      arr.push(i)
    }
    console.log(arr)
    return await Promise.all(arr)
}

By performing two separate queries i am getting proper result but i want to combine these two queries.

One more problem with above one is, output of result1 is not giving promised output for last fetching(or matching).

Edit:-

{ "_id" : 1, "friends" : [2,3,4], "closedFriends":[5,6,7] }
{ "_id" : 2, "friends" : [ 1,3,5 ], "closedFriends" : [4,6,7] }

In the above database the common friends between 1 and 2 is 3 and closedFriends are 6 and 7. The desired output is cfcount is 2 and fcount is 1 along with ids.

Edit2:-

{ "_id" : 1, "friends" : [{id:1, name:'john', score:0}, {id:2, name:'john', score:0}, {id:3, name:'john', score:0}], "closedFriends":[{id:8, name:'john', score:0}, {id:4, name:'john', score:0}, {id:5, name:'john', score:0}] }
{ "_id" : 2, "friends" : [{id:2, name:'john', score:0}, {id:7, name:'john', score:0}, {id:3, name:'john', score:0} ], "closedFriends" : [{id:1, name:'john', score:0}, {id:9, name:'john', score:0}, {id:8, name:'john', score:0}] }

Here along with count I want to include filed 'name' and 'score' in aggregated output.

Upvotes: 0

Views: 198

Answers (1)

TechWisdom
TechWisdom

Reputation: 4285

The aggregation framework can replace all your Javascript calculations (your for loop counting), and thus eliminate o(2n) aggregational computations you did, into only one o(1).

function uinfo(data, user){
    let mainUser = db.Profile.findOne({_id: user});
    let userIds = Array.from(data, user => user.id);
    return Profile.aggregate([
    {$match: {'_id': {$in: userIds}}},
    {
        $project: {
            '_id' : 1,
            'fcount': {
                $size: {
                    $setIntersection: [
                        mainUser.friends,
                        '$friends',
                    ]
                }
            },
            'cfcount': {
                $size: {
                    $setIntersection: [
                        mainUser.closedFriends,
                        '$closedFriends',
                    ]
                }
            }
        }
    },
    ])
}

Upvotes: 1

Related Questions