如何基于多对多关系select用户子集?
How to select subset of users based on many-to-many relationship?
User
和 Organization
通过 Relationship
.
存在多对多关联
Relationship
模型中的一个变量是一个名为 member
的布尔值。
user
可以在 Relationship
模型中多次出现,因为 user
可以与多个 organizations
.
相关
我如何 select 来自所有 users
的随机实例 1) 与任何 organization
没有关系(所以不要出现在 Relationship
模型) 加上 2) 那些 users
确实与组织有关系但 member = false
对于 所有 他们与组织的关系?
我在想类似的东西:
user = User.where( relationship_id = nil || ??? ).offset(rand(0..100)).first
嗯,使用 ActiveRecord 并不意味着您被禁止编写 SQL 代码。实际上,在许多情况下,您需要这样做。您的情况似乎是其中之一(您不能使用 ActiveRecord 进行 'OR' 查询,并且您没有 SQL 'WHERE EXISTS' 的语法糖)。
所以我建议你这样做:
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)
有关 SQL 的更多信息 "EXISTS":
http://www.techonthenet.com/sql/exists.php
UPD。发布了另一个使用 where_exists
gem.
的答案
获取没有组织的用户:
User.where.not(id: Relationship.pluck(:user_id).uniq)
如果没有一些逻辑来比较用户的总关系与 member = false 的关系,另一个查询似乎不是您可以做的事情。
因此,您真正想要的是不属于任何组织的用户,其中 "being a member" 表示 "having a relationship join record where member = true"。这是一个比您指定的条件简单得多的概念。
在那种情况下:
- 从 member = true
的关系中区分 user_id
- 获取 ID 不在此列表中的用户
例如
member_ids = Relationship.where(member: true).distinct.pluck(:user_id)
@users = User.where("id not in (?)", member_ids).all
我会这样做
User.joins("LEFT JOIN relationships ON relationships.user_id = users.id").where('relationships.user_id IS NULL').offset(rand(0..100)).first
类似于:
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
现在有一个 Where Exists gem 可供您使用。 (完全披露:我最近创建了 gem。)
# 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
User
和 Organization
通过 Relationship
.
Relationship
模型中的一个变量是一个名为 member
的布尔值。
user
可以在 Relationship
模型中多次出现,因为 user
可以与多个 organizations
.
我如何 select 来自所有 users
的随机实例 1) 与任何 organization
没有关系(所以不要出现在 Relationship
模型) 加上 2) 那些 users
确实与组织有关系但 member = false
对于 所有 他们与组织的关系?
我在想类似的东西:
user = User.where( relationship_id = nil || ??? ).offset(rand(0..100)).first
嗯,使用 ActiveRecord 并不意味着您被禁止编写 SQL 代码。实际上,在许多情况下,您需要这样做。您的情况似乎是其中之一(您不能使用 ActiveRecord 进行 'OR' 查询,并且您没有 SQL 'WHERE EXISTS' 的语法糖)。
所以我建议你这样做:
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)
有关 SQL 的更多信息 "EXISTS":
http://www.techonthenet.com/sql/exists.php
UPD。发布了另一个使用 where_exists
gem.
获取没有组织的用户:
User.where.not(id: Relationship.pluck(:user_id).uniq)
如果没有一些逻辑来比较用户的总关系与 member = false 的关系,另一个查询似乎不是您可以做的事情。
因此,您真正想要的是不属于任何组织的用户,其中 "being a member" 表示 "having a relationship join record where member = true"。这是一个比您指定的条件简单得多的概念。
在那种情况下:
- 从 member = true 的关系中区分 user_id
- 获取 ID 不在此列表中的用户
例如
member_ids = Relationship.where(member: true).distinct.pluck(:user_id)
@users = User.where("id not in (?)", member_ids).all
我会这样做
User.joins("LEFT JOIN relationships ON relationships.user_id = users.id").where('relationships.user_id IS NULL').offset(rand(0..100)).first
类似于:
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
现在有一个 Where Exists gem 可供您使用。 (完全披露:我最近创建了 gem。)
# 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