How to select subset of users based on many-to-many relationship?

北城以北 提交于 2019-12-13 17:03:20

问题


User and Organization have a many-to-many association through Relationship.

One of the variables in the Relationship model is a boolean called member.
A user can occur in the Relationship model multiple times as a user can be related to multiple organizations.

How can I select a random instance from all the users that 1) don't have a relationship with any organization (so don't occur in the Relationship model) plus 2) those users that do have a relationship with an organization but for whom member = false for all their relationships with organizations?

I was thinking of something like:

user = User.where( relationship_id = nil || ??? ).offset(rand(0..100)).first

回答1:


I will do this

  1. User.joins("LEFT JOIN relationships ON relationships.user_id = users.id").where('relationships.user_id IS NULL').offset(rand(0..100)).first

  2. Something like:

    member_ids = Relationship.where(member: true).pluck(:user_id).uniq
    users = User.where.not(id: member_ids) # or User.where('id NOT in (?)', member_ids) on Rails < 4 
    




回答2:


So, what you actually want is users who aren't a member of any organisations, where "being a member" means "having a relationship join record where member = true". This is a much simpler concept than the conditions you specified.

In that case:

  • get distinct user_id from relationships where member = true
  • get users whose id IS NOT in this list

eg

member_ids = Relationship.where(member: true).distinct.pluck(:user_id)
@users = User.where("id not in (?)", member_ids).all



回答3:


For getting Users with no Organization:

User.where.not(id: Relationship.pluck(:user_id).uniq)

The other query doesn't seem like something you could be doing without some logic to compare the total Relationships for a User against those where member = false.




回答4:


There is now a Where Exists gem which you can use. (Full disclosure: I've created that gem recently.)

# This will select all users, for which there are not relationships,
# or there is a relationship, but its 'member' attributes is false.
#
# Just what you've asked for.
users_to_select = User.where_not_exists(:relationships, member: true)

# 'offset' and 'count' will work as usual after 'where_not_exists'
random_user = users_to_select.offset(rand(users_to_select.count)).first



回答5:


Well, using ActiveRecord doesn't presume that you are prohibited to write SQL code. Actually, you are required to do so in many cases. Your case seems to be one of these (you can't do 'OR' query with ActiveRecord, and you there is no syntax sugar for SQL 'WHERE EXISTS').

So I suggest you do something like:

free_users = User.where("NOT EXISTS (SELECT 1 FROM relationships WHERE user_id = users.id) OR NOT EXISTS (SELECT 1 FROM relationships WHERE user_id = users.id AND member = FALSE)")
random_user = User.find(free_users.ids.sample)

More info on SQL "EXISTS":

http://www.techonthenet.com/sql/exists.php

UPD. Posted another answer which utilizes where_exists gem.



来源:https://stackoverflow.com/questions/31749281/how-to-select-subset-of-users-based-on-many-to-many-relationship

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!