如何动态地将连接表与源进行比较

时间:2015-12-22 13:12:25

标签: mysql ruby-on-rails ruby activerecord arel

上下文

我有五种模式:奖励,品牌订阅,品牌,级别和用户。

  • Brand有很多Tiers
  • BrandSubscription属于TierUser
  • Reward属于Tier
  • Tier有一个名为order的属性。如果BrandSubscription具有更高级别的等级,它也将具有所有较低等级。
  • BrandSubscription可以查看其Rewards中的所有Tiers,在这种情况下,Tier所属的Tiers及其所有has_many :rewards BrandSubscription

问题

我的问题在于上面列表的最后一项。我试图获得品牌订阅的所有奖励。

理想的方式是through: :tier实体上的Tier,即has_many :rewards。在order我可以has_many :rewards。此方法的问题在于奖励不限于当前层,但必须包括来自较低Tier层的所有奖励。

为实现这一目标,我将范围放在class Tier < ActiveRecord::Base belongs_to :brand has_many :rewards has_many :with_lower_tiers, lambda { where(this_table[:order].gteq(that_table[:order])) }, through: :brand, source: :tiers has_many :achievable_rewards, through: :with_lower_tiers, source: rewards end 模型的this_table上:

that_table

此处的问题是class Tier < ActiveRecord::Base belongs_to :brand has_many :rewards has_many :with_lower_tiers, lambda { |tier| where(current_scope.table[:order].lteq(tier.order)) }, through: :brand, source: :tiers has_many :achievable_rewards, through: :with_lower_tiers, source: rewards end tier。我需要在这里进行某种联接,所以我可以在表格之间进行比较。我可以做的一种方法是:

BrandSubscription

这里我使用了层所有者对象并获得了它的顺序。这里的问题是我不能真正依赖BrandSubscription.joins(:with_lower_tiers)论证。跟随查询已经中断,因为作为参数传递给范围函数的是&#34;所有者&#34;查询的实体,在本例中为tiers

SELECT DISTINCT rewards.* FROM tiers INNER JOIN brand_subscriptions ON tiers.id = brand_subscriptions.tier_id INNER JOIN tiers tiers_reward ON tiers_reward.brand_id = tiers.brand_id INNER JOIN rewards ON tiers_reward.id = rewards.tier_id WHERE tiers_reward.order <= tiers.order AND brand_subscriptions.user_id = 1234

我想得到的SQL查询如下,我可以从用户那里获得所有可用的奖励。请注意,我两次加入{{1}}表,这正是我遇到麻烦的地方:

{{1}}

我相信一些Arel可能会有所帮助,但如果我完全依赖ActiveRecord,我真的很乐意,因为代码会更清晰。

参考

我使用以下链接尝试解决此问题:

1 个答案:

答案 0 :(得分:0)

您可以自己构建查询,而不是依赖复杂的has_many-through关联

class Reward < ActiveRecord::Base
  belongs_to :tier
end

class Brand < ActiveRecord::Base
  has_many :tiers
end

class Tier < ActiveRecord::Base
  belongs_to :brand
  has_many :rewards

  # has_many :lower_tiers, ->(tier){ where('tiers.order < ?', tier.order) }, through: :brand, source: :tiers

  def lower_tiers
    Tier.where('brand_id = ? AND order < ?', brand_id, order)
  end

  def achievable_rewards
    Rewards.where('tier_id IN (?) OR tier_id = ?', lower_tiers.select(:id), id)
  end
end


class BrandSubscription < ActiveRecord::Base
  belongs_to :user
  belongs_to :tier

  def rewards
    tier ? tier.achievable_rewards : Reward.none
  end
end