Home > Enterprise >  MongoDB Aggregate: count occurrences in array of object and transform to object
MongoDB Aggregate: count occurrences in array of object and transform to object

Time:06-02

I have a collection called notifications that has 2 fields (for simplicity):

  • targets is an array of strings

  • userReads is an array of object {userId, readAt}

Example :

{
   targets: ['group1', 'group2'],
   userReads: [
     {userId: 1, readAt: 'date1'},
     {userId: 2, readAt: 'date2'},
     {userId: 3, readAt: 'date3'},
   ]
}

{
   targets: ['group1'],
   userReads: [
     {userId: 1, readAt: 'date4'}
   ]
}

DESIRED OUTPUT:

{
   groupsNotificationsCount: {
     group1: 2,
     group2: 1
   },
   usersNotificationsCount: {
     1: 2,
     2: 1,
     3: 1
   }
}

At first I tried this aggregate:

[{
 $match: {
  targets: {
   $in: ['group/1','group/2']
  },
 }
}, {
 $project: {
  targets: 1,
  userReads: 1
 }
}, {
 $unwind: {
  path: '$targets'
 }
}, {
 $match: {
  targets: {
   $in: ['group/1','group/2']
  }
 }
}, {
 $group: {
  _id: '$targets',
  countForGroup: {
   $count: {}
  },
  userReads: {
   $push: '$userReads'
  }
 }
}, {
 $addFields: {
  userReads: {
   $reduce: {
    input: '$userReads',
    initialValue: [],
    'in': {
     $concatArrays: [
      '$$value',
      '$$this'
     ]
    }
   }
  }
 }
}]

I have the right counts for each group in my documents like so:

{
   _id: 'group1',
   countForGroup: 2,
   userReads: [
     {userId: 1, readAt: 'date1'},
     {userId: 2, readAt: 'date2'},
     {userId: 3, readAt: 'date3'},
     {userId: 1, readAt: 'date4'},
   ]
}

But now I have no idea how to go on from there to get my desired output

CodePudding user response:

This is a perfect use case for using $facet. You can process groupsNotificationsCount and usersNotificationsCount separately by $unwind and $group the count. Afterwards by wrangling into array of k-v tuples, you can construct your expected form by using $arrayToObject

db.collection.aggregate([
  {
    "$facet": {
      "groupsNotificationsCount": [
        {
          "$unwind": "$targets"
        },
        {
          $group: {
            _id: "$targets",
            count: {
              $sum: 1
            }
          }
        }
      ],
      "usersNotificationsCount": [
        {
          "$unwind": "$userReads"
        },
        {
          $group: {
            _id: "$userReads.userId",
            count: {
              $sum: 1
            }
          }
        }
      ]
    }
  },
  {
    "$project": {
      groupsNotificationsCount: {
        "$arrayToObject": {
          "$map": {
            "input": "$groupsNotificationsCount",
            "as": "g",
            "in": {
              k: "$$g._id",
              v: "$$g.count"
            }
          }
        }
      },
      usersNotificationsCount: {
        "$arrayToObject": {
          "$map": {
            "input": "$usersNotificationsCount",
            "as": "u",
            "in": {
              k: {
                $toString: "$$u._id"
              },
              v: "$$u.count"
            }
          }
        }
      }
    }
  }
])

Here is the Mongo playground for your reference.

  • Related