使用多个belongs_to关联多次连接到同一模型

我有一个类协议,它有一个与成员类的两个belongs_to关联 – 引用为主要和次要:

class Agreement < ActiveRecord::Base belongs_to :primary, class_name: 'Member' belongs_to :secondary, class_name: 'Member' ... def self.by_member(member_attribute_hash) # returns any agreements that has a primary OR secondary member that matches any of the values # in the member_attribute_hash ... end end 

会员类不了解与协议类的关联 – 它不需要:

 class Member < ActiveRecord::Base # contains surname, given_names, member_number ... def self.by_any(member_attribute_hash) # returns any member where the member matches on surname OR given_names OR member_number ... end end 

我想要做的是搜索主要或次要成员符合一组标准的所有协议。

从以前的工作( 参见问题#14139609 ),我已经解决了如何为Member.by_any()构建条件where子句。

在搜索协议时尝试重用该方法让我尝试这样做:

 class Agreement < ActiveRecord::Base ... def self.by_member(member_attribute_hash) Agreement.joins{primary.outer}.merge(Member.by_any(member_attribute_hash)).joins{secondary.outer}.merge(Member.by_any(member_attribute_hash)) end end 

在控制台中使用member_attribute_hash = {surname: 'Freud'} ,生成的SQL无法遵守为第二次成员连接生成的别名:

 SELECT "agreements".* FROM "agreements" LEFT OUTER JOIN "members" ON "members"."id" = "agreements"."primary_id" LEFT OUTER JOIN "members" "secondarys_agreements" ON "secondarys_agreements"."id" = "agreements"."secondary_id" WHERE "members"."surname" ILIKE 'Freud%' AND "members"."surname" ILIKE 'Freud%' 

请注意WHERE子句中的重复条件。 这将返回协议,其中主要成员具有类似“弗洛伊德”的姓氏,但忽略次要成员条件,因为别名没有流经合并。

有任何想法吗?

在努力理解这一点之后,我最终用Squeel sifter替换了Member.by_any范围:

 class Member < ActiveRecord::Base # contains surname, given_names, member_number ... def self.by_any(member_attribute_hash) # returns any member where the member matches on surname OR given_names OR member_number squeel do [ (surname =~ "#{member[:surname]}%" if member[:surname].present?), (given_names =~ "#{member[:given_names]}%" if member[:given_names].present?), (member_number == member[:member_number] if member[:member_number].present?) ].compact.reduce(:|) # compact to remove the nils, reduce to combine the cases with | end end end 

唯一的区别(代码方式),在sifterscope是用squeel中的squeel替换scopewhere

因此,我现在可以从协议模型中引用Member :by_any sifter,而不是使用merge来从协议模型访问Member.by_any范围。 它看起来像:

 class Agreement < ActiveRecord::Base ... def self.by_member(member_attribute_hash) Agreement.joins{primary.outer}.where{primary.sift :by_any, member_attribute_hash}.joins{secondary.outer}.where{secondary.sift :by_any, member_attribute_hash} end end 

这解决了别名问题 - 开始庆祝!

 SELECT "agreements".* FROM "agreements" LEFT OUTER JOIN "members" ON "members"."id" = "agreements"."primary_id" LEFT OUTER JOIN "members" "secondarys_agreements" ON "secondarys_agreements"."id" = "agreements"."secondary_id" WHERE "members"."surname" ILIKE 'Freud%' AND "secondarys_agreements"."surname" ILIKE 'Freud%' 

但我仍然没有得到我预期的结果 - 庆祝活动被搁置 。 哪里错了? where子句中的AND是错误的。 经过一段时间的挖掘(和电脑一夜之后),一个精神焕发的头脑决定试试这个:

 class Agreement < ActiveRecord::Base ... def self.by_member(member_attribute_hash) Agreement.joins{primary.outer}.joins{secondary.outer}.where{(primary.sift :by_any, member_attribute_hash) | (secondary.sift :by_any, member_attribute_hash)} end end 

制作这个:

 SELECT "agreements".* FROM "agreements" LEFT OUTER JOIN "members" ON "members"."id" = "agreements"."primary_id" LEFT OUTER JOIN "members" "secondarys_agreements" ON "secondarys_agreements"."id" = "agreements"."secondary_id" WHERE ((("members"."surname" ILIKE 'Freud%') OR ("secondarys_agreements"."surname" ILIKE 'Freud%'))) 

啊甜蜜...... 重新开始庆祝 ......现在我得到的协议有一个主要或次要成员,根据单个筛子中定义的规则进行匹配。

对于他在Squeel所做的所有工作,对Ernie Miller大肆宣传。