Rails:如何在不进行N + 1查询的情况下渴望加载有序关联的有限记录

时间:2019-06-12 15:14:51

标签: ruby-on-rails ruby database activerecord

我知道有关这些主题的问题很多,但我没有一个涵盖所有方面的问题。

考虑UserActivityLike模型。查询活动时,我希望为集合中的每个活动加载第一个[赞] ,而不进行N + 1次查询,并且不加载不必要的记录。我的代码如下所示:

class User < ActiveRecord::Base
  has_many :likes, as: :liker
end

class Activity < ActiveRecord::Base
  has_many :likes
  has_one :first_like, -> { order(created_at: :asc) }, class_name: "Like"
end

class Like < ActiveRecord::Base
  belongs_to :activity
  belongs_to :liker, polymorphic: true
end

我做了全面的要旨来测试不同的加载策略和方法:https://gist.github.com/thisismydesign/b08ba0ee3c1862ef87effe0e25386267

策略:N + 1个查询,左外部联接,单个额外查询

方法:eager_loadincludesincludes & referencesincludes & preload(这些结果将导致左外部联接或单个额外查询)

这是我发现的问题:

  • 左外部联接在关联范围内也不尊重order(created_at: :asc)(参见rails issue)。它确实遵守显式排序,即default_scope { order(created_at: :asc) }
  • 但是,应避免使用左外部联接,因为它“可能导致许多行包含冗余数据,并且在规模上表现不佳”(请参阅​​:apidoc rubydocrails api)。这是一个真正的问题,即使在双方都进行索引搜索的情况下,数据也很多。
  • 单个额外的查询将创建一个无限制的查询,有可能获取大量数据(请参阅:rails issue
  • 为该关联添加一个显式.order("likes.created_at asc"),希望限制单个额外的查询会破坏事情

首选方法是仅查询所需记录的单个额外查询。总而言之,我找不到Rails的本地解决方案。有吗?

1 个答案:

答案 0 :(得分:0)

在我的问题中,我正在寻找使用Rails的本地方法。但是,这是使用SQL和虚拟属性的解决方案:

class Activity < ApplicationRecord
  has_one :first_like, class_name: "Like", primary_key: :first_like_id, foreign_key: :id

  scope :with_first_like, lambda {
    select(
      "activities.*,
      (
        SELECT like_id as first_like_id
        FROM likes
        WHERE activity_id = activities.id
        ORDER BY created_at ASC
        LIMIT 1
      )"
    )
  }
end

Activity.with_first_like.includes(:first_like)