Grouping returned documents

I am getting a list of users like so:

db.users.aggregate([
         {
            $match: {
                $or: [
                    {
                    _id: {
                      $in: [targetUserId, currentUserId]
                    }
                  },
                    {
                        $and: [
                          {
                            $or: [
                              { "field.filter": false },
                              { "field2.filter": true }
                            ]
                          },
                            { "profile.memberOrgId": "orgId" },
                            {$project: { 
                                      name: "$profile.name",
                                      ...
                           } }
                          ]
                         }
                    ],
                }
        }
])

How can I format \ transform the results to looks like this:

{
  currentUser: { current user object },
  targetUser: { target user object },
  members: [ {array of users that match the  { "profile.memberOrgId": "orgId" } filter }]
}

Thanks.

Are you able to provide some sample input documents and the current output documents from the aggregation pipeline provided? This will make it easier for any user here to insert to a test environment to try assist.

Additionally, please confirm the MongoDB version in use.

Regards,
Jason

MongoDB version 5.0

Documents are as follows (Pre projection):

{
  _id: "document id",
email: {
  address: "email@mail.com",
  verified: Boolean
 },
  profile: {
     name: "User  Name",
     memberOrgId: "Org ID"
  }
}

during projection I change them as follows (Post projection):

{
  _id: "document id",
  name: "$profile.name",
  memberOrgId: "$profile.memberOrgId",
  emailAddress: "$email.address"
}

Thanks.

1 Like

Thanks.

So you get the list of users and then project them to the advised format? I assume after this, you then want to transform this to the following?:

From the post-projection, what field indicates that it is a current user or target user?

Regards,
Jason

The tagertUserId and currentUserId which are _id's would determine the currentUser and targetUser from the list of returned users.

Thanks.

Sample documents I created (based off the post-projection you provided):

/// documents with _id of 1, 2 and 6 have the same `memberOrgId` value
[
  {
    _id: 1,
    name: 'Example Name 1',
    memberOrgId: 'org1',
    emailAddress: 'example1@email.com'
  },
  {
    _id: 3,
    name: 'Example Name 3',
    memberOrgId: 'org3',
    emailAddress: 'example3@email.com'
  },
  {
    _id: 4,
    name: 'Example Name 4',
    memberOrgId: 'org4',
    emailAddress: 'example4@email.com'
  },
  {
    _id: 5,
    name: 'Example Name 5',
    memberOrgId: 'org5',
    emailAddress: 'example5@email.com'
  },
  {
    _id: 6,
    name: 'Example Name 6',
    memberOrgId: 'org1',
    emailAddress: 'example6@email.com'
  },
  {
    _id: 2,
    name: 'Example Name 2',
    memberOrgId: 'org1',
    emailAddress: 'example2@email.com'
  }
]

I’m not 100% sure of the expected output but does something like the below work?

Note: I only tested this on documents post-projection in my test environment. I do not know what the actual initial documents would look like.

I’m assuming currentUserId and currentUserId are known values since you use them in your initial $match stage.

/// Get all members that have the matching orgId
{
  $match: {
    memberOrgId: 'org1'
  }
},
/// using $project and $cond to display _id (member), memberOrgId, currentUser and targetUser (assuming _id values for currentUser and targetUser are known (1 and 2 in this case))
{
  $project: {
    _id:1,
    memberOrgId:1,
    currentUser : {
      $cond: {
        if : {$eq: ['$_id',1]},
        then: '$$CURRENT',
        else: '$$REMOVE'
      }
    },
    targetUser : {
      $cond: {
        if : {$eq: ['$_id',2]},
        then: '$$CURRENT',
        else: '$$REMOVE'
      }
    }
  }
},
/// $group all (assuming there is only 1 unique currentUser and 1 unique targetUser document)
{
  $group: {
    _id: null,
    currentUser : {$max:'$currentUser'},
    targetUser : {$max:'$targetUser'},
    members : {$push:'$_id'}
  }
}

Resulting in:

[
  {
    _id: null,
    currentUser: {
      _id: 1,
      name: 'Example Name 1',
      memberOrgId: 'org1',
      emailAddress: 'example1@email.com'
    },
    targetUser: {
      _id: 2,
      name: 'Example Name 2',
      memberOrgId: 'org1',
      emailAddress: 'example2@email.com'
    },
    members: [ 1, 6, 2 ]
  }
]

For reference, the $cond documentation which may be of use.

If the above doesn’t suit your use case or requirements, please provide:

  1. use case details
  2. full aggregation in use currently
  3. 4-5 sample documents
  4. expected output based off the 4-5 sample documents

There might be a simpler way to achieve this but it’s difficult without any sample documents and without knowing the expected output.

I’d advise testing on a test environment to verify it suits your use case and/or requirement(s).

Regards,
Jason

Thanks very much @Jason_Tran , your suggestion put me on the right path.

Here is what I’ve come up with:

db.users.aggregate([
        {
            $match: {
                $or: [
                    {
                    _id: {
                      $in: [targetUserId, currentUserId]
                    }
                  },
                    {
                        $and: [
                          {
                            $or: [
                              { "profile.receiveEmails": true },
                              { "profile.receiveNotifications": true }
                            ]
                          },
                            { "profile.memberOrgId": "orgId" },
        
            ]
                    }
                    ],
                }
            
        },
        {$project: {
            firstName: "$profile.firstName",
            receiveEmails: "$profile.receiveEmails",
            receiveNotifications: "$profile.receiveNotifications",
            emailAddress: "$emails.address"
        }},
        {
            $group: {
              "_id": "Users",
              "currentUser": {
                "$max": {
                  $cond:[
                        { $eq: ["$_id", currentUserId] },
                        "$$CURRENT",
                        "$$REMOVE"
                    ]
                }
              },
            "targetUserId": {
                "$max": {
                  $cond:[
                        { $eq: ["$_id", targetUserId] },
                        "$$CURRENT",
                        "$$REMOVE"
                    ]
                }
              },
            "members": {
                "$addToSet": {
                  $cond:[
                        { $ne: ["$_id", targetUserId] },
                        "$$CURRENT",
                        "$$REMOVE"
                    ]
                }
              }
            }
        }
    ])

This gives me exactly what I was looking for.

Thanks again for your help!

2 Likes

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.