When we delete a user it’s user_id will be automatically removed from all role.user_ids arrays. And when we delete role that role_id will be removed from all user.role_ids.

inverse_of: nil

But then our our system grows with tens of thousands of users in each role. We do not want to store all those user_ids in role. We can modify has_and_belongs_to_many like this to only store role_ids in user record.

The problem is when we delete Role record it’s ObjectId will remain in all User.role_ids. Since there is no relationship defined in Role model the default callbacks do not fire. So we need to create a custom callback to remove specific role ObjectId from ALL user.role_ids. Instead of array.push we will use array.pull.

The downside with this approach is it will cause count queries against Users collection for specific roles on each user save. We can make this approach smarter by checking if roles changed and then incrementing/decrementing role.users_count.

Having these custom callbacks can complicate the application (lead to bugs) so I prefer using traditional two sided has_and_belongs_to_many.

Roles array

Another way we can store this data is to create a simple array on the user record and store roles as strings. To get users by role we create scopes on User model (User.role1, User.role2)

This approach is fine if we have a fairly fixed number of roles. But what if we want to store something like tags for our users? The only difference is we do not restrict tags field to specific enumeration and change scope to accept tags parameter.