【问题标题】:Adding related data to ActiveRecords without using a join在不使用连接的情况下将相关数据添加到 ActiveRecords
【发布时间】:2015-09-01 00:52:35
【问题描述】:

我的 Rails 4 应用有一个User 模型、一个Link 模型和一个Hit 模型。每个User 有很多Links,每个Link 有很多Hits。有时,我想显示UserLinks 的列表以及它拥有的Hits 的数量。

执行此操作的明显方法是遍历链接并在每个链接上调用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")
}

这有效地为每个Link 添加了一个虚拟hit_count 属性,该属性在单个查询中计算。奇怪的是,它似乎是与加载链接分开的查询,而不是实际在同一个查询中完成:

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 ;

不幸的是,随着hits 表的增长,这已成为一个缓慢的查询。 EXPLAIN 表示查询正在使用索引将 所有 匹配链接与其匹配的链接连接起来,然后通过顺序扫描将链接缩小到具有正确 user_id 的链接;这似乎是它很慢的原因。但是,如果我们已经单独加载了链接列表——而且我们是——根本不需要加入链接表。我们可以获取用户的链接 ID 列表,然后使用hits.link_id IN (list of IDs) 纯粹对命中表进行查询。

将其编写为单独的查询很容易,而且运行速度极快:

Hit.where(link_id: @user.links.ids).group(:link_id).count

问题是,我不知道如何让 ActiveRecord 作为 Link 模型上的范围来执行此操作,以便每个 Link 都有一个我可以使用的 hit_count 属性,所以我可以使用生成的返回值作为关系,并能够将其他查询链接到它上面。有什么想法吗?

(我知道 ActiveRecord 的 counter_cache 功能,但我不想在这里使用它——hits 由单独的非 Ruby 系统插入,修改该系统以更新计数器缓存将是中度疼痛。)

【问题讨论】:

  • 您是否考虑过使用数据库视图并为其添加 hit_count 属性?在数据库级别上的处理速度比在 activerecord 级别上快得多。

标签: ruby-on-rails join scope rails-activerecord


【解决方案1】:

正如 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

然后在模型中

class Hit < ActiveRecord::Base

  belongs_to :link, :counter_cache => true   #this will trigger the rails magic

如果你有更复杂的东西,你可以自己写,这很简单。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多