Home > Software design >  In MongoDB - trying to perform a recursive lookup between 2 collections where 1 collection has a nes
In MongoDB - trying to perform a recursive lookup between 2 collections where 1 collection has a nes

Time:01-25

The scenario is I have 2 collections - 1 with a list of users and 1 with a list of groups.

"users": [
    {
      "_id": 1,
      "username": "Fred Smith",
      "level": 12,
    },
    {
      "_id": 2,
      "username": "Bob Brown",
      "level": 20,
    },
    {
      "_id": 3,
      "username": "Joe Blogs",
      "level": 1
    }
  ],

A group holds a list of users but crucially also potentially holds groups within the same list as well.

"groups": [
    {
      "_id": 1,
      "groupname": "admin",
      "members": ["Fred Smith"]
    },
    {
      "_id": 2,
      "groupname": "users",
      "members": [
        "Fred Smith",
        "Bob Brown",
        "contractors"
      ]
    },
    {
      "_id": 3,
      "groupname": "contractors",
      "members": ["Joe Blogs"]
    },
   {
      "_id": 4,
      "groupname": "all",
      "members": ["users"]
    }]

Given a query of a specific username I need to return all groups that user is a member of.

So far I have the $graphLookup working in the following playground - but I am not sure of the next step to take into account the nested group name inside that list of users. There is also the possibility the groups may nested further groups.

db.users.aggregate([
  {
    $match: {
      "username": "Joe Blogs"
    }
  },
  {
    "$graphLookup": {
      "from": "groups",
      "startWith": "$username",
      "connectFromField": "username",
      "connectToField": "members",
      "as": "groups",
      "maxDepth": 10
    }
  },
  {
    $project: {
      "username": 1,
      "groups": "$groups.groupname"
    }
  }
]

https://mongoplayground.net/p/GBRJp42bdjt

[
  {
    "_id": 3,
    "groups": [
      "contractors"
    ],
    "username": "Joe Blogs"
  }
]

In this example I have queried Fred who is a member of the contractors group but the contractors group is also a member of the users group and the users group is a member of the all group.

So I would need the query to return both contractors (which it does now) but also users and all.

It feels like given an initial array of groups in this query I then need to do a different query with the array of groups to find out what groups they are a member of.

I could modify the structure if required to make this easier but the nesting of groups is a requirement.

Any help appreciated.

As an update I have created another query which given an array of groups will return a normalised list of all the nested groups - https://mongoplayground.net/p/9NGG_wIJkGX

Now I just need to work out how to feed the results of the first query to the second query - if anyone has an idea I would appreciate it.

CodePudding user response:

Your first playground is actually very close already. You just need to use groupname in connectFromField in the $graphLookup to traverse the collection. At the last $project stage, use $reduce with $setUnion to extract all the groups from the $graphLookup result.

db.users.aggregate([
  {
    $match: {
      "username": "Joe Blogs"
    }
  },
  {
    "$graphLookup": {
      "from": "groups",
      "startWith": "$username",
      "connectFromField": "groupname",
      "connectToField": "members",
      "as": "groups",
      "maxDepth": 10
    }
  },
  {
    $project: {
      "username": 1,
      "groups": {
        "$reduce": {
          "input": "$groups",
          "initialValue": [],
          "in": {
            "$setUnion": [
              "$$value",
              [
                "$$this.groupname"
              ]
            ]
          }
        }
      }
    }
  }
])

Mongo Playground

  • Related