我有这样的模型,具有多态关系
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相关的资源。
你能帮我解决这个问题吗?
有可能从未点击资源,但我必须获得资源以及每个级别至少需要一个资源 所以我认为内部联接应该改为左外部
答案 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”中的第一个)希望它有所帮助!