如何获取大多数点击的记录和至少一个子级资源将包含

时间:2016-06-13 08:46:47

标签: mysql ruby-on-rails-4 activerecord polymorphic-associations

我有这样的模型,具有多态关系

class Level1
 has_and_belongs_to_many :level2s
 has_many :resources ,:as => :mediable
end

class Level2
    has_and_belongs_to_many :level1s
    has_many :level3s
    has_many :resources ,:as => :mediable
end

class Level3
    belongs_to :level2
    has_many :resources ,:as => :mediable
end

class Resource
    belongs_to :mediable , polymorphic: true
    has_many :resources ,:as => :mediable
    has_many :clicks ,:as => :mediable
end

class click
    belongs_to :clickable , polymorphic: true
end

当用户在level1 / level2 / level3(图像或媒体)中添加资源时,我会在某处显示这些媒体,用户可以点击此处,每次点击我都会在点击表中保存一个条目

现在我需要在level1的显示页面上的用户时,我需要根据点击次数显示level1s和level2s的前50个资源,并且至少有一个资源将从数据库中提取

我将尝试这样:

Resource.select("resources.*, count(clicks.id) as click_counts")
            .joins( "INNER JOIN clicks ON clicks.clickable_id = resources.id AND clicks.clickable_type='Resource'" )
            .where("(resources.mediable_id IN(1) AND resources.mediable_type='Level1') OR (resources.mediable_id IN(1, 2, 3, 4, 5) AND resources.mediable_type='Level2')")
            .group("resources.id")
            .order("click_counts").limit(50)

它将返回与级别1及其相关级别2相关的前50个资源,但不能保证我至少有一个与级别2相关的资源。

你能帮我解决这个问题吗?

有可能从未点击资源,但我必须获得资源以及每个级别至少需要一个资源 所以我认为内部联接应该改为左外部

1 个答案:

答案 0 :(得分:1)

如果您不需要进行额外的计算,可以通过以下方式轻松实现:

resources = Resource.select("resources.*, count(clicks.id) as click_counts")
            .joins( "INNER JOIN clicks ON clicks.clickable_id = resources.id AND clicks.clickable_type='Resource'" )
            .where("(resources.mediable_id IN(1) AND resources.mediable_type='Level1') OR (resources.mediable_id IN(1, 2, 3, 4, 5) AND resources.mediable_type='Level2')")
            .group("resources.id")
            .order("click_counts").limit(50)

unless resources.pluck(:mediable_type).include? 'Level2'

  resources = resources.limit(49) + Resource.select("resources.*, count(clicks.id) as click_counts")
            .joins( "INNER JOIN clicks ON clicks.clickable_id = resources.id AND clicks.clickable_type='Resource'" )
            .where("resources.mediable_id IN(1, 2, 3, 4, 5) AND resources.mediable_type='Level2'")
            .group("resources.id")
            .order("click_counts").limit(1)
end

否则,您可以尝试这样的事情(未经测试):

sub_select = "(
   SELECT resources.*, count(clicks.id) as click_counts, 1 as SortKey
   FROM resources 
   INNER JOIN clicks ON clicks.clickable_id = resources.id AND clicks.clickable_type='Resource'
   WHERE resources.mediable_id IN(1, 2, 3, 4, 5) AND resources.mediable_type='Level2'
   GROUP BY resources.id
   ORDER BY mediable_type, click_counts 
   LIMIT 1

   UNION ALL

   SELECT resources.*, count(clicks.id) as click_counts, 2 as SortKey
   FROM resources 
   INNER JOIN clicks ON clicks.clickable_id = resources.id AND clicks.clickable_type='Resource'
   WHERE (resources.mediable_id IN(1) AND resources.mediable_type='Level1') OR (resources.mediable_id IN(1, 2, 3, 4, 5) AND resources.mediable_type='Level2')
   GROUP BY resources.id
   ORDER BY SortKey, mediable_type, click_counts) as t"

select_sql = "SELECT DISTINCT resources.*, click_counts FROM #{sub_select} LIMIT 50"

results = ActiveRecord::Base.connection.select_all(select_sql).rows # array
results_ordered = results.sort { |a, b| a.last <=> b.last } # sort by clicks_count

注意:

  • SortKey额外属性保证查询顺序
  • UNION ALL不会消除任何重复记录。因此,我们需要在DISTINCT列上添加resources.*, click_counts以删除可能的重复记录(“Level2”中的第一个)

希望它有所帮助!