Я знаю, что есть много вопросов по некоторым из этих тем, но я не нашел один, охватывающий все аспекты.
Рассмотрим модели User
, Activity
и Like
. Когда я запрашиваю действие, я бы хотел загрузить первый лайк для каждого действия в коллекции , не делая 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_load
, includes
, includes & references
, includes & preload
(это приведет либо к левому внешнему соединению, либо к одному дополнительному запросу)
Вот проблемы, которые я обнаружил:
- Левое внешнее соединение не учитывает
order(created_at: :asc)
в области ассоциаций и default_scope { order(created_at: :asc) }
(см .: рельсовый выпуск ). Он действительно соблюдает явное упорядочение, т.е. .order("likes.created_at asc")
.
- Тем не менее следует избегать левого внешнего соединения, поскольку оно «может привести к тому, что во многих строках будут содержаться избыточные данные, и оно плохо работает в масштабе» (см .: apidoc rubydoc , рельсы api ). Это реальная проблема с большим количеством данных даже при индексированных поисках с обеих сторон.
- Один дополнительный запрос создаст запрос без ограничений, потенциально получая огромные объемы данных (см .: rails отпуск )
- Добавление явного
limit(1)
к ассоциации в надежде на ограничение одного дополнительного запроса приведет к поломке
Предпочтительным методом будет один дополнительный запрос, который запрашивает только необходимые записи. В общем, я не мог найти нативное решение с Rails. Есть ли?