Стремительная загрузка: правильный способ делать вещи - PullRequest
20 голосов
/ 10 марта 2012

Я использую Ruby on Rails 3.1.Я прочитал следующие статьи и документацию о нетерпеливой загрузке , и я хотел бы найти правильный способ сделать что-то:

  1. Стремительно загружаемых ассоциаций [Официальная документация]
  2. ActiveRecord :: Ассоциации :: ClassMethods (см. Раздел «Загрузка загрузок ассоциаций») [Официальная документация]
  3. Загрузка загрузок [Статья в блоге]

# 2 говорит:

Обратите внимание, что при использовании таких условий, как Post.include ([: author,: comments]). where (['comments.approved =?', true]). все могут иметь непредвиденные последствия.

# 3 говорит, что эти непреднамеренные последствияare ( note : примеры практически одинаковы, поэтому я цитирую точный текст статьи в блоге, но вы должны учитывать обходной путь, а не конкретную реализацию):

Этот запрос, поскольку он будет использовать LEFT JOIN, также отбрасывает все сообщения без комментариевСлово «первый» в любом из его комментариев.

То есть, если есть не «связанные» объекты, «основной связанный» объект не будет загружен.Это то, что происходит, когда я пытаюсь использовать энергичную загрузку путем добавления некоторого условия , например .where(:category_relationships => {:user_id => @current_user.id}) в моем предыдущем вопросе , но я не хочу, чтобы это произошло.

Итак (пораженец, потому что я, вероятно, не могу использовать энергичную загрузку в моем случае, когда условие не может быть установлено в операторе has_many - обратите внимание, что в приведенном выше коде @current_user.id is "устанавливается динамически«в отличие от примеров, представленных на упомянутых сайтах), Я хотел бы знать, существуют ли методики / приемы / стратегии для ограничения запросов к базе данных, поскольку у меня« проблема N + 1 ».

Возможно, эти методики / приемы / стратегии могут быть реализованы с использованием инфраструктуры Ruby on Rails вообще ... # 1 говорит:

Даже если Active Record позволяет вам задавать условиядля быстро загружаемых ассоциаций, таких как объединения, рекомендуется вместо этого использовать объединения.

Что и как решить эту проблему правильным образом?


Может быть, сольЗадача состоит в том, чтобы извлекать и создавать самостоятельно то, что должно быть загружено, запуская определенные и отдельные запросы к базе данных, но тогда проблема будет состоять в том, как "передать" / "связать" / "интерполировать" эти извлеченные "связанные" объекты в«основной связанный» объект, чтобы их можно было использовать «простой загрузкой»? То есть, как сделать возможным (см. упомянутый вопрос для получения дополнительной информации) использовать код, подобный @article.comments и получать только те комментарии, которые я загружал сам?После моей нетерпеливой загрузки, возможно ли * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} *} *} * * * * * * * *} * * * * * * * * * * * * * поддается пометитьстатьи?

Ответы [ 2 ]

6 голосов
/ 23 августа 2012

После моей энергичной загрузки возможно / правильно сделать что-то вроде @ article.comments = my_eager_loaded_comments, чтобы "передавать" / "связывать" / "интерполировать" комментарии к статьям?

Да, это возможно.Я делаю это регулярно.

Обратите внимание, что мое решение по-прежнему получает ВСЕ связанные объекты из БД.Я не думаю, что есть какое-либо решение для извлечения только отфильтрованных объектов ассоциации, если ваше состояние является динамическим.Мое решение сфокусировано на фильтрации найденных объектов ассоциации.

Я предполагаю, что требуется get a list of articles, а в каждой статье eager load the comments of only one particular user.

В модели Article:

def self.get_articles_with_comments_of_user( article_ids, user_id )
  articles = Article.where( id: article_ids ).includes( :comments )

  articles.each do |article|
    filtered_comments = article.comments.select { |comment| comment.user_id == user_id }
    article.association("comments").target = filtered_comments
  end

  articles
end

В коллекции статей, возвращаемых вышеуказанным методом, ассоциация комментариев будет содержать только комментарии этого конкретного пользователя.

Надеюсь, это то, что вы просите.

2 голосов
/ 12 марта 2012

От # 2 ActiveRecord :: Associations :: ClassMethods приходит так:

Если вы действительно хотите загружать только некоторые члены ассоциации, обычно более естественно включитьассоциация, для которой определены условия:

class Post < ActiveRecord::Base
  has_many :approved_comments,
    :class_name => 'Comment', 
    :conditions => ['approved = ?', true]
end

Post.includes(:approved_comments)

Это будет загружать сообщения и нетерпеливо загружать ассоциацию утвержденных_комментариев, которая содержит только те комментарии, которые были утверждены.*

Если я правильно понимаю это в контексте, есть некоторая неоднозначность, когда вы применяете .where () к своему основному запросу с помощью include (), и AR применяет везде, где весь запрос ограничивает результаты вашего принципа теми, которые имеютсвязанные предикаты.Но если вы объявляете предикат только для ассоциации, как описано выше, AR понимает и предоставляет вам все ваши основные результаты и те связанные объекты, которые соответствуют их условию предиката.

...