0

我有一个类协议,其中有两个属于成员类的关联 - 被称为主要和次要:

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

Member 类不知道与 Agreement 类的关联——它不需要:

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)中,我整理出了如何构建条件 where 子句Member.by_any()

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

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 子句中的重复条件。这将返回主要成员具有“弗洛伊德”等姓氏的协议,但忽略次要成员条件,因为别名未通过合并。

有任何想法吗?

4

1 回答 1

0

在努力理解这一点之后,我最终用Member.by_anySqueel 替换了示波器sifter

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在 the和 the之间的唯一区别(代码方面)是将作用域中scope的 替换为sifter 中的。wheresqueel

因此,我现在能够从协议模型中引用筛选器,而不是使用 a从协议模型merge中访问范围。它看起来像:Member.by_anyMember :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%'

但我仍然没有得到我预期的结果——庆祝活动被搁置了。什么问题?ANDwhere 子句中的错误。经过更多的挖掘(并且远离计算机一晚),一个精神焕发的头脑决定尝试这个:

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 大受好评。

于 2013-03-07T15:53:50.600 回答