【问题标题】:Rails preload query via joined table filterRails 通过连接表过滤器预加载查询
【发布时间】:2015-03-26 08:27:59
【问题描述】:

我正在查询一堆由人们撰写和接收的信件。我通过连接表将字母表与人员表相关联,该连接表使用人员类型标识符(0 = 创建者,1 = 收件人)确定该人是相关信件的创建者还是收件人。

我的问题:我在用户视图中一次显示 50 封信,其中包含日期、创建者、收件人和博物馆收藏等信息。页面加载速度非常慢,因为尽管我已经想出了如何“包含”集合,以便不对正在显示的每个字母运行查询,但我无法为创建者和收件人执行此操作。如果我只是简单地显示“人”,我也可以做一个包含,但由于我按他们的角色过滤他们,rails 对视图中的每个字母运行几个查询,这大约需要 6000 毫秒。如果没有创建者和接收者,视图会在大约 250 毫秒内加载。

以下是我的模型的基础知识:

class ArchiveObject < ActiveRecord::Base
    has_many :archive_object_people
    has_many :people, :through => :archive_object_people

class ArchiveObjectPerson < ActiveRecord::Base
    belongs_to :archive_object
    belongs_to :person

class Person < ActiveRecord::Base
    has_many :archive_object_people
    has_many :archive_objects, :through => :archive_object_people

我还在模型中创建了一些范围和方法来帮助我。 在archive_object.rb中

# These can be used to distinguish recipients from creators of letters
# Use case:  ArchiveObject.first.creators
def recipients
  people.where("archive_object_people.persontype = '1'")
end
def creators
  people.where("archive_object_people.persontype = '0'")
end

scope :recipients, lambda {
  joins(:people).where("archive_object_people.persontype = '1'")
}
scope :creators, lambda {
  joins(:people).where("archive_object_people.persontype = '0'")
}

我想预加载创建者和接收者,就像我包含字母的位置一样,这样我就可以遍历每个字母并显示 letter.title、letter.creators、letter.collection 等,而无需运行数十个查询。例如:ArchiveObject.includes(:collections, :creators)

到目前为止我已经尝试过的事情。所有这些都将在获取收件人/创建者时运行另一个查询,尽管他们在查看通用“人”时可能不会运行另一个查询:

ArchiveObject.joins(:people).where("archive_object_people.persontype = '1'").preload(:people)

ArchiveObject.includes(:people, :archive_object_people).first.recipients

ArchiveObject.includes(:recipients)  => raises an exception

我还开始阅读有关重新定义 has_many 并使用条件仅拉动创建者或接收者的文章,但实际上我需要两个关联,在这种情况下:一个 has_many 创建者和一个 has_many 接收者。我不知道如何设置,但如果有人有任何想法,那就太棒了。

感谢您给我的任何帮助或建议!我很想弄清楚这一点,以便我拥有一个超级快速和高效的网站。 :)

【问题讨论】:

    标签: sql ruby-on-rails optimization eager-loading


    【解决方案1】:

    好的。我花了几个小时才弄明白!

    我删除了所有自定义实用程序方法并重写了一些 has_many 类型的东西。

    class ArchiveObject < ActiveRecord::Base
      has_many :archive_object_people
      has_many :people, :through => :archive_object_people
    
      has_many :creators, -> { where "archive_object_people.persontype = '0'" },
               :through => :archive_object_people
               :source => :person
      has_many :recipients, -> { where "archive_object_people.persontype = '1'" },
               :through => :archive_object_people,
               :source => :person
    

    通过模型中的以下内容,您可以执行以下操作:

    test = ArchiveObject.includes(:people, :creators, :recipients)
    test.first.people  # => list of all people on first object
    test.first.creators # => list of all people who are creators on first object
    test.first.recipients # => all people who are recipients
    

    我之前可以使用自定义方法获取该信息,但现在使用包含您可以预加载它们,以便它预先运行大量查询,然后在提取每个字母的信息时不需要再次重新运行它。

    我希望这对其他人有所帮助。请注意,has_many 描述中的 :source 是 :person,而不是 :people,这给我带来了一些暂时的痛苦。

    【讨论】:

      猜你喜欢
      • 2022-11-11
      • 2019-05-21
      • 1970-01-01
      • 2015-09-09
      • 1970-01-01
      • 1970-01-01
      • 2019-07-22
      • 2011-07-11
      • 2016-08-06
      相关资源
      最近更新 更多