使用多个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
唯一的区别(代码方式),在sifter
和scope
是用squeel
中的squeel
替换scope
的where
。
因此,我现在可以从协议模型中引用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大肆宣传。