【问题标题】:Eager loading: The right way to do things急切加载:做事的正确方式
【发布时间】:2012-03-27 10:13:18
【问题描述】:

我正在运行 Ruby on Rails 3.1。我阅读了以下关于eager loading的文章和文档,我想找到一种正确的方法来做事:

  1. Eager Loading Associations【官方文档】
  2. ActiveRecord::Associations::ClassMethods(参见“渴望加载关联”部分)[官方文档]
  3. Eager loading[博文]

#2 说:

请注意,使用 Post.includes([:author, :cmets]).where(['cmets.approved = ?', true]).all 之类的条件可能会产生意想不到的后果。

#3 表示这些意外后果是(注意:示例几乎相同,因此我引用了博客文章的确切文字,但您必须保留注意解决方法,而不是具体的实现):

这个查询,因为它会使用 LEFT JOIN,所以也会丢弃所有在它的任何 cmets 上没有带有“first”字样的评论的帖子。

也就是说,如果存在非“关联”对象,则不会加载“主要关联”对象。这就是我尝试通过在我的previous question添加一些条件(如.where(:category_relationships => {:user_id => @current_user.id}))来使用急切加载时发生的情况,但我不希望这种情况发生。

所以(失败者,因为在我无法在 has_many 语句中设置 条件 的情况下,我可能无法使用即时加载 - 请注意,在上面的代码中 @current_user.id 是“动态设置”,与上述网站中的示例不同),我想知道是否有实践/技术/策略,以便限制数据库查询,因为我有一个“N + 1 问题”。 p>

也许这些实践/技术/策略完全可以通过使用 Ruby on Rails 框架来实现……#1 说:

即使 Active Record 允许您在 Eager 上指定条件 加载关联就像连接一样,推荐的方法是使用 而是加入。

什么以及如何以正确的方式解决这个问题?


也许一个解决方案是通过运行特定和分离的数据库查询来检索和构建自己需要加载的内容,但是问题将是如何“传递”/“关联”/“插入”那些检索到的“关联”对象到“主要关联”对象,以便那些可以使用“急切加载”的方式?也就是说,如何使可能(有关更多信息,请参阅mentioned question)使用像@这样的代码987654329@ 并且只获得我渴望自己加载的 cmets?在 我的 急切加载之后,是否 可能 / 正确 制作像 @article.comments = my_eager_loaded_comments 这样的东西来“通过”/“关联”/“插值” " cmets 到文章?

【问题讨论】:

    标签: ruby-on-rails ruby database ruby-on-rails-3 eager-loading


    【解决方案1】:

    在我急切加载之后,是否可以/正确地制作类似 @article.cmets = my_eager_loaded_cmets 的内容以便“传递”/“关联”/“插值”cmets 到文章?

    是的,这是可能的。我经常这样做。

    请注意,我的解决方案仍会从数据库中检索所有关联的对象。如果您的条件是动态的,我认为没有任何解决方案可以仅检索过滤的关联对象。我的解决方案侧重于对检索到的关联对象进行过滤。

    我假设要求是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
    

    在上述方法返回的文章集合中,cmets 关联将只有该特定用户的 cmets。

    我希望这是您的要求。

    【讨论】:

    • “如果您的条件是动态的,我认为没有任何解决方案可以仅检索过滤的关联对象。” - 也许this 是解决方案?
    • 在链接的答案中,我没有看到传递任何动态条件来过滤急切加载。我可能理解错了。该答案仅描述了一个普通的has_many :through 关联。在这个问题的上下文中如何使用该解决方案?
    【解决方案2】:

    来自#2 ActiveRecord::Associations::ClassMethods 来自:

    如果您确实希望只加载关联的某些成员,则可以 通常更自然地包含有条件的关联 在上面定义:

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

    这将加载帖子并急切加载approved_cmets关联, 其中仅包含那些已被批准的 cmets。

    如果我在上下文中正确理解这一点,那么当您使用包含()将 .where() 应用于您的主查询时会出现歧义,并且 AR 将 where 应用于整个查询,将您的主要结果限制为具有相关资格的结果谓词。但是,如果您将谓词限定为上述关联,AR 会理解并为您提供所有主要结果以及与谓词条件匹配的关联对象。

    【讨论】:

    • 我不能像你写的那样使用has_many 关联,因为在我的情况下,我应该声明has_many, :conditions =&gt; ['user_id = ?', @current_user.id] 之类的东西(这是不可能的! ...当然据我所知)。所以我想知道与急切加载功能相关的那个问题的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    • 2017-10-19
    • 1970-01-01
    相关资源
    最近更新 更多