设置。
我正在尝试通过RoR' ActiveRecord::Relation
实现复杂的搜索查询。
到目前为止,index
操作代码中的代码如下所示:
query = Member.all
query = query.search(params[:q])
query = query.includes(:structures)
query = query.with_structure(params[:party_id])
query = query.with_structure(params[:assembly_id])
@members = query.paginate(:page => params[:page])
代码背后的想法是我获得所有成员,然后在过滤器处于活动状态时删除成员。 search
和with_structures
是自定义类方法,其中执行where
子句。
旁注:因此,如果您的解决方案涉及使用arel
,请确保向我展示如何将AREL :: NODES转换回ActiveRecord :: Relation
我的数据库如下所示:
会员可以改变他在一个结构中的位置,因此他/她可以有多个参与同一个结构。
问题
当party
和assembly
过滤器都处于活动状态时,查询不返回任何内容,因为它在同一列上执行多个where子句。
我尝试了什么
Member.joins(:structures)
.where(structures: { id: [1303, 1181] })
.group("members.id").count
或者您更喜欢SQL:
SELECT COUNT(*) AS count_all, members.id AS members_id
FROM "members"
INNER JOIN "participations" ON "participations"."member_id" = "members"."id"
INNER JOIN "structures" ON "structures"."id" = "participations"."structure_id"
WHERE "structures"."id" IN (1303, 1181)
GROUP BY members.id
HAVING count(*)>1
这不起作用,因为许多记录都有一个> 1,但只是两个结构中的一个的一部分,使用OUTER LEFT JOIN也没有帮助。
我也尝试使用arel
,但我无法转换回ActiveRecord。我尝试使用q1.intersect q2
。
我想要什么
我希望能够同时应用where语句,而不会相互抵消。我希望能够写出这样的东西:
query = Member.all
q1 = query.with_structure(params[:party_id])
q2 = query.with_structure(params[:assembly_id])
query = q1.intersect q2
提前感谢您,如果不清楚,请不要随意询问排队。 :)
答案 0 :(得分:2)
您必须两次加入参与,每个ID一次。
from members m join participation p1
on m.id = p1.member_id and p1.structure_id = 1303
join participation p2 on m.id = p2.member_id and p2.structure_id = 1181
我不确定你是否需要加入结构,因为你似乎没有选择它。
答案 1 :(得分:2)
我想与SO分享我作为解决问题的方法。我不会接受我自己的回答作为答案,因为@Dan Bracuk对我有所帮助而且不公平。
在控制器中:
query = Member.all
query = query.search(params[:q])
ids = params.slice(:party_id, :assembly_id).values.delete_if { |v| v.blank? }.map { |v| v.to_i }
query = query.create_joins ids
@members = query.paginate(:page => params[:page])
这是在成员模型中:
def self.create_joins structure_ids
if structure_ids.empty?
all
else
structure_ids.each_with_index.map do |id, idx|
joins("INNER JOIN 'participations' 'p#{idx}' ON 'p#{idx}'.'member_id' = 'members'.'id'")
.where("p#{idx}.structure_id" => id).uniq
end.reduce(:merge)
end
end
我们的想法是,我们为sructure_ids
内的每个ID创建一个单独的连接和查询,然后使用merge
方法将它们reduce
放在一起。