Home > Software design >  Combine array values from collection and $lookup result
Combine array values from collection and $lookup result

Time:11-18

I have two collections, resource-policies and role-permissions. I'm trying to get all the permissions for resource1, including the ones that are defined in the matching record from role-permissions.

resource-policies

{
  resource: 'resource1',
  permissions: [
    'permission:read',
    'role:admin'
  ]
}

role-permissions

{
  name: 'role:admin',
  permissions: ['permission:write', 'permission:delete']
}

Desired Result

[
  {
    resource: 'resource1',
    permissions: [
      'permission:read', 
      'permission:write',  
      'permission:delete'
    ]
  }
]

Here's my current attempt. I'm stuck on the $map projection to create the final combinded permissions field.

resourcePoliciesCollection.aggregate([
  {
    $match: {
      $expr: {
        $eq: ['$resource', resource] 
      },
    },
  },
  {
    $lookup: {
      from: 'role-permissions',
      localField: 'permissions',
      foreignField: 'permissions',
      as: 'rolePermissions',
    },
  },
  {
    $project: {
      _id: -1,
      permissions: {
        $map: {
          input: '$permissions',
          in: {
            $cond: [
              { $eq: ['$$this', '$rolePermissions.name'] },
              '$rolePermissions.permissions',
              '$$this',
            ],
          },
        },
      },
      resource: 1,
    },
  }
])

But this produces the following, which is without the permissions from role-permissions.

{
  "_id": ObjectId("5a934e000102030405000000"),
  "permissions": [
    "permission:read",
    "role:admin"
  ],
  "resource": "resource1"
}

Mongo Playground

CodePudding user response:

Your schema is suggesting a recursive structure. I prefer using $graphLookup for this reason and perform $setUnion to get all the permissions. Finally do a filter to remove the role entries(i.e. entries starting with "role:")

db.resourcepolicies.aggregate([
  {
    $match: {
      $expr: {
        $eq: [
          "$resource",
          "resource1"
        ]
      }
    }
  },
  {
    "$graphLookup": {
      "from": "rolepermissions",
      "startWith": "$permissions",
      "connectFromField": "permissions",
      "connectToField": "name",
      "as": "rpLookup"
    }
  },
  {
    "$project": {
      _id: 0,
      resource: 1,
      permissions: {
        "$reduce": {
          "input": "$rpLookup",
          "initialValue": "$permissions",
          "in": {
            "$setUnion": [
              "$$value",
              "$$this.permissions"
            ]
          }
        }
      }
    }
  },
  {
    "$addFields": {
      "permissions": {
        "$filter": {
          "input": "$permissions",
          "as": "p",
          "cond": {
            // remove permissions entry which contains "role:"
            $eq: [
              -1,
              {
                "$indexOfCP": [
                  "$$p",
                  "role:"
                ]
              }
            ]
          }
        }
      }
    }
  }
])

Mongo Playground

  • Related