问题
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
User.joins("LEFT JOIN relationships ON relationships.user_id = users.id").where('relationships.user_id IS NULL').offset(rand(0..100)).first
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