Home > database >  Calculate Bitwise Operators Value
Calculate Bitwise Operators Value

Time:08-25

I have a ranks collection with a permissions field which are bitwise operators:

[
  {
    "_id": "xxxx",
    "name": "Rank 1",
    "permissions": 1
  },
  {
    "_id": "xxxxxxxxx",
    "name": "Rank 2",
    "permissions": 2
  }
]

Example users:

[
   {
      "_id":"1234",
      "ranks":[
         "xxxx",
         "xxxxxxxxx"
      ]
   }
]

The users collection containts a ranks value, which stores an array of the rank ids.

I'm wanting to get the user, and their ranks and set their permissions to a value.

const users = await this.collection.aggregate([
    {
        $match: { userID: '123' }
    },
    { $limit: 1 },
    {
        $lookup: {
            from: 'ranks',
            localField: 'rank',
            foreignField: '_id',
            as: 'ranks'
        }
    },
    {
        $set: {
            permissions: {
                $arrayElemAt: ['$rank.permissions', 0]
            }
        }
    },
    {
        $unwind: {
            path: '$rank',
            preserveNullAndEmptyArrays: true
        }
    }
]).toArray();

This obviously gets 1 value from the collection, I'm wanting to get all permissions and add the bitwise operators together.

Expected Output

{
   "_id": "1234",
   "ranks":[
        "xxxx",
        "xxxxxxxxx"
   ]
   "permissions":3
}

Any help is appreciated!

CodePudding user response:

Here's one way to "or" all the rank permissions by using a server-side javascript "$function".

db.users.aggregate([
  {
    "$match": {
      "_id": 42
    }
  },
  {
    "$lookup": {
      "from": "ranks",
      "localField": "ranks",
      "foreignField": "_id",
      "pipeline": [
        {
          "$project": {
            "_id": 0,
            "permissions": 1
          }
        }
      ],
      "as": "permissions"
    }
  },
  {
    "$set": {
      "permissions": {
        "$function": {
          "body": "function(perms) {return perms.reduce((prevV, currV) => prevV | currV, 0)}",
          "args": ["$permissions.permissions"],
          "lang": "js"
        }
      }
    }
  }
])

Try it on mongoplayground.net.

CodePudding user response:

With sample collection...

db = {
  "permissions": [
    {
      "_id": "xxxx",
      "name": "Rank 1",
      "permissions": 1
    },
    {
      "_id": "xxxxxxxxx",
      "name": "Rank 2",
      "permissions": 2
    },
    {
      "_id": "xxxxxxx",
      "name": "Rank 4",
      "permissions": 4
    }
  ],
  "users": [
    {
      "_id": "1234",
      "ranks": [
        "xxxx",
        "xxxxxxxxx"
      ]
    },
    {
      "_id": "4567",
      "ranks": [
        "xxxx",
        "xxxxxxx"
      ]
    }
  ]
}

...try the following aggregation, which...

  • Finds the _id for the user 1234 in the users collection.
  • Looks for all the corresponding ranks in the permissions collection.
  • Unwinds to have one result per corresponding permission.
  • Aggregates the permissions and ranks.
db.users.aggregate([
  {
    $match: {
      "_id": "1234"
    }
  },
  {
    $lookup: {
      from: "permissions",
      localField: "ranks",
      foreignField: "_id",
      as: "ranks"
    }
  },
  {
    $unwind: "$ranks"
  },
  {
    $group: {
      _id: "$_id",
      ranks: {
        $push: "$ranks._id"
      },
      permissions: {
        $sum: "$ranks.permissions"
      }
    }
  }
])

See MongoDB playground at...

Important Note: This query groups the permissions by sum (rather than by boolean logical OR), so you must ensure that there are no duplicate permissions. If you can't ensure unique permissions per user, then suggest that the permissions are $pushed like the ranks, and then perform some post processing on the list of permissions to reduce via logical OR...

  • Related