避免在具有不同关系的rails STI上进行n + 1查询

时间:2018-03-13 14:19:59

标签: ruby-on-rails sti jbuilder select-n-plus-1

所以,让我说我有以下型号:

class Building < ActiveRecord::Base
  has_many :rooms
  has_many :training_rooms, class_name: 'TrainginRoom', source: rooms
  has_many :computers, through: :training_rooms
end

class Computer < ActiveRecord::Base
  belongs_to :room
end

class Room < ActiveRecord::Base
  belongs_to :building
end

class Office < Room
end

class TrainingRoom < Room
  has_many :computers
end

并且还让我说我遵循jsonapi规范并使用包含的顶级成员在单个http调用中呈现每个相关对象。

所以建筑/展览看起来像这样:

json.data do
  json.id building.id
  json.type building.type
  json.attributes do
    # render the attributes
  end
  json.relationships do
    # render the relationships
  end
end

json.included.do
  json.array! building.rooms.each do |room|
    json.type room.type
    json.id  room.id
    json.attributes do
     # render the attribtues
    end

    json.relationships do |room|
      # render included member's relationships
      # N+1 Be here
    end
  end
end

我无法急切地从包含的成员加载关系,因为它不存在于阵列的所有成员上。

例如,在控制器中:

@building = Building.eager_load(
  { rooms: :computers }
).find(params[:id])

如果房间关系中有办公室,则不起作用,因为它没有计算机关系。

@building = Building.eager_load(
  :rooms,
  traning_rooms: :computers
).find(params[:id])

同样不起作用,因为培训室关系提供了对侧载计算机的访问,但是不能直接在渲染代码中访问,因此是无用的缓存。

此外,我尝试将默认范围应用于培训室以急切加载计算机关联,但这也没有产生预期效果。

我现在唯一能想到的就是将计算机关系应用到Room课程,但我并不是真的想要这样做,因为只有训练室应该有电脑。

我喜欢听到任何想法。

1 个答案:

答案 0 :(得分:0)

由于计算机模型中没有training_room_id,因此在定义关系时必须明确提及foreign_key

class TrainingRoom < Room
  has_many :computers, foreign_key: :room_id
end

现在您可以急切地加载记录了:

@building = Building.eager_load({ training_rooms: :computers }).where(id: params[:id])

希望它有所帮助!