Ruby on rails 在不使用联接的情况下向ActiveRecords添加相关数据
我的Rails 4应用程序有一个Ruby on rails 在不使用联接的情况下向ActiveRecords添加相关数据,ruby-on-rails,join,scope,rails-activerecord,Ruby On Rails,Join,Scope,Rails Activerecord,我的Rails 4应用程序有一个用户模型、一个链接模型和一个点击模型。每个用户都有许多链接s,每个链接都有许多点击s。有时,我想显示用户的链接的列表,以及它的点击次数 显而易见的方法是在链接上循环并对每个链接调用link.hits.count,但这会产生N+1个查询。因此,我编写了一个作用域,它连接hits表: scope :with_hit_counts, -> { joins("LEFT OUTER JOIN hits ON hits.link_id = links.id").se
用户
模型、一个链接
模型和一个点击
模型。每个用户
都有许多链接
s,每个链接
都有许多点击
s。有时,我想显示用户的链接的列表,以及它的点击次数
显而易见的方法是在链接上循环并对每个链接调用link.hits.count
,但这会产生N+1个查询。因此,我编写了一个作用域,它连接hits
表:
scope :with_hit_counts, -> {
joins("LEFT OUTER JOIN hits ON hits.link_id = links.id").select('links.*', 'count(hits.link_id) AS hit_count').group("links.id")
}
这有效地为每个链接添加了一个虚拟的点击次数
属性,该属性在单个查询中计算得到。奇怪的是,它似乎是加载链接的一个单独查询,而不是在同一个查询中实际执行:
SELECT COUNT(*) AS count_all, links.id AS links_id
FROM "links" LEFT OUTER JOIN hits ON hits.link_id = links.id
WHERE "links"."user_id" = $1
GROUP BY links.id
ORDER BY "links"."domain_id" ASC, "links"."custom_slug" ASC, "links"."id" ASC ;
不幸的是,随着点击
表的增长,这已成为一个缓慢的查询EXPLAIN
表示查询使用索引将所有点击与其匹配的链接连接起来,然后通过顺序扫描将链接缩小到只有正确的user\u id
的链接;这似乎就是它慢的原因。但是,如果我们已经单独加载了链接列表,并且已经加载了,那么实际上根本不需要加入links表。我们可以为用户获取链接id列表,然后使用(id列表)
中的hits.link\u id在hits表上进行纯粹的查询
将其作为单独的查询编写很容易,而且运行速度极快:
Hit.where(link_id: @user.links.ids).group(:link_id).count
问题是,我不知道如何让ActiveRecord在链接
模型上作为一个作用域来做这件事,这样每个链接
都有一个我可以使用的命中计数
属性,这样我就可以使用得到的返回值作为一个与链接其他查询的能力相关的关系。有什么想法吗
(我确实知道ActiveRecord的计数器缓存
功能,但我不想在这里使用它-命中
是由一个单独的非Ruby系统插入的,修改该系统以更新计数器缓存会有一定的痛苦。)如trh所述,通常的做法是在link.rb中添加一个hits_count,然后您可以快速查询和排序。你只需要让它们保持同步。如果它确实是一个基本计数,那么您可以使用基本rails计数器缓存。这将在创建和销毁时递增和递减
class AddCounterCacheToLink < ActiveRecord::Migration
def up
add_column :links, :hits_count, :integer, :default => 0
Link.reset_column_information
Link.all.each do |l|
l.update_attribute :hits_count, l.hits.size
end
end
end
类AddCounterCacheToLink0
Link.reset\u列\u信息
Link.all.each do|l|
l、 更新属性:点击次数,l.hits.size
结束
结束
结束
然后在模型中
class Hit < ActiveRecord::Base
belongs_to :link, :counter_cache => true #this will trigger the rails magic
类命中true#这将触发rails魔法
如果您有更复杂的内容,您可以编写自己的,这很简单。正如trh所提到的,通常的方法是将点击数添加到link.rb,然后您可以快速查询和排序。你只需要让它们保持同步。如果它确实是一个基本计数,那么您可以使用基本rails计数器缓存。这将在创建和销毁时递增和递减
class AddCounterCacheToLink < ActiveRecord::Migration
def up
add_column :links, :hits_count, :integer, :default => 0
Link.reset_column_information
Link.all.each do |l|
l.update_attribute :hits_count, l.hits.size
end
end
end
类AddCounterCacheToLink0
Link.reset\u列\u信息
Link.all.each do|l|
l、 更新属性:点击次数,l.hits.size
结束
结束
结束
然后在模型中
class Hit < ActiveRecord::Base
belongs_to :link, :counter_cache => true #this will trigger the rails magic
类命中true#这将触发rails魔法
如果您有更复杂的内容,您可以编写自己的,这很简单。您是否考虑过使用数据库视图,并向其添加hit\u count属性?在处理速度比activerecord快得多的数据库级别上。您是否考虑过使用数据库视图,并向其添加hit_count属性?在数据库级别,处理速度比activerecord级别快得多。