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

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

# 2 говорит:

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

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

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

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

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

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

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

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


Может быть, решение состоит в том, чтобы извлекать и строить себя, что нужно загружать, запуская конкретные и разделенные запросы к базе данных, но тогда проблема заключалась бы в том, как "передавать" / "ассоциировать" / "интерполировать" найденные "связанные" объекты к "основному ассоциированному" объекту, чтобы можно было использовать способ "посторонней загрузки"? То есть, как сделать возможным (см. упомянутый вопрос для получения дополнительной информации) использовать код типа @article.comments и получать только те комментарии, которые я загрузил самостоятельно? После моей загруженной загрузки возможно ли/правильно сделать что-то вроде @article.comments = my_eager_loaded_comments, чтобы "передавать" / "ассоциировать" / "интерполировать" комментарии к статьям?

Ответ 1

После моей активной загрузки возможно ли/правильно сделать что-то вроде @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

От # 2 ActiveRecord:: Ассоциации:: ClassMethods приходит следующее:

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

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

Post.includes(:approved_comments)

Это будет загружать сообщения и загружать ассоциацию authorized_comments, который содержит только те комментарии, которые были одобрены.

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