链接has_many的子句:通过关联(Rails 4.1.0.rc1)

时间:2014-03-09 19:59:07

标签: mysql ruby-on-rails ruby-on-rails-3 activerecord ruby-on-rails-4

背景:我处于边缘Rails(4.1.0.rc1)。用户通过CommunityUser模型拥有许多社区。以下用户属于各种社区:

USERS TABLE
ID | COMMUNITY_IDS
---|--------------
1  | [2, 7, 8]
2  | [3, 4, 8]
3  | [4, 5, 7]
4  | [3, 5, 7]
5  | [3, 8, 10]
6  | [4, 6, 7]
7  | [1, 8, 10]
8  | [3, 8, 10]
9  | [2, 9, 10]
10 | [3, 6, 10]

User.joins(:社区).where(社区:{id:[5,7]})。uniq

  

返回与社区5或社区7关联的所有用户:

     

SQL => SELECT DISTINCT“users”。* FROM“users”INNER JOIN   “community_users”ON“community_users”。“user_id”=“users”。“id”INNER   加入“社区”ON“社区”。“id”=   “community_users”。“community_id”在哪里“社区”。“id”IN(5,7)

ID | COMMUNITY_IDS
---|--------------
1  | [2, 7, 8]
3  | [4, 5, 7]
4  | [3, 5, 7]
6  | [4, 6, 7]

尝试通过添加另一个where子句来返回空的ActiveRecord :: Relation来进一步过滤这些(以返回来自此组的那些也与社区6相关联):

User.joins(:社区).where(社区:{id:[5,7]})。其中(社区:{id:[6]})。uniq

  

=> SQL:SELECT DISTINCT“users”。* FROM“users”INNER JOIN“community_users”ON“community_users”。“user_id”=“users”。“id”INNER   加入“社区”ON“社区”。“id”=   “community_users”。“community_id”在哪里“社区”。“id”IN(5,7)   和“社区”。“id”IN(6)

=> #<ActiveRecord::Relation []>

目标:这是此查询的正确行为吗?如果是这样,我将如何编写此查询以返回与社区6相关联的 EITHER 社区5或社区7 AND 相关联的用户。

2 个答案:

答案 0 :(得分:4)

问题是你在某种程度上考虑了social_ids,你在上面显示。

ID | COMMUNITY_IDS
---|--------------
1  | [2, 7, 8]
3  | [4, 5, 7]
4  | [3, 5, 7]
6  | [4, 6, 7]

实际上,您将获得此中间结果集:

UID| COMMUNITY_ID
---|--------------
3  | 5
4  | 5
1  | 7
3  | 7
4  | 7
6  | 7

然后你说:好的,和“社区”。“id”IN(6)! 结果集中没有具有此类ID的社区。

为用户提供社区[5,7]:

users = Community.where(id:[5,7]).map(&:users).flatten.uniq

按社区ID = 7过滤用户:

filtered = users.select{|u| u.communities.map(&:id).include?(6) }

此代码有效(它返回具有id = 6的单个用户的数组) 但这不是最佳解决方案。

答案 1 :(得分:1)

此解决方案要求您自己编写连接查询,而不是使用Ruby。另一方面,这只是一个(快速)SQL查询,如果你要对社区进行大量比较,它会更好地扩展。

正如Vitalyp指出的那样,CommunityUsers表看起来像这样

CommunityUsers
user_id | community_id
--------|--------------
 1      | 5
 2      | 5
 1      | 7
 2      | 7
 1      | 6
 3      | 6

为了找到包含社区5或7以及6的记录,我们可以选择并加入它们。

User.joins('INNER JOIN "community_users" c1 ON c1."user_id" = "users"."id" AND c1."id" IN [5, 7]')
    .joins('INNER JOIN "community_users" c2 ON c2."user_id" = "users"."id" AND c2."id" = 6')

使用内部联接,查询仅选择社区5或社区6(第二个连接子句)中的社区5或7(第一个连接子句)中的用户。


其他提示:在community_users.user_id上创建索引也会加快查询速度。