在使用 includes Rails 进行预加载时使用范围合并方法

Use merge method with scope while eager loading with includes Rails

这是在 codeschool on Scopes, as well as watching a video by Chris Oliver 观看有关合并方法的视频的后续问题。

我想做的是只找到那些至少有一个 book 可用的 authors。然后在过滤那些 authors 之后,我想为那些选择的 authors 加载所有 books 因为我不想每次提取关于这些的数据时都查询数据库图书。我尝试了多种不同的示波器,但其中 none 正是我所需要的:

#app/models/book.rb 
class Book < ActiveRecord::Base
  belongs_to :author

  scope :available, ->{where(availability: true)}
  scope :unavailable, ->{where(availability: false)}
end

#app/models/author.rb
class Author < ActiveRecord::Base
  has_many :books, dependent: :destroy

  scope :with_available_books, ->{joins(:books).merge(Book.available)}
  scope :with_available_books_uniq, ->{uniq.joins(:books).merge(Book.available)}
  scope :with_available_books_includes, ->{joins(:books).merge(Book.available).includes(:books)}
  scope :with_available_books_uniq_includes, ->{uniq.joins(:books).merge(Book.available).includes(:books)}

  def to_s
    self.name
  end
end

这是我的数据库中内容的快照 我有 三个 authors:

  1. Neil,他总共有 10 本相关书籍,全部可用
  2. John,他总共有10本相关书籍,全部不可用
  3. Mixture Author,他一共10本书,5本可用,5本不可用

我运行所有查询,结果输出在HTML。这是我得到的:

# Duplicating the authors AND N + 1 problem with associated books

Author.with_available_books.size: 15
作者[0]的书籍:10
作者[1]的书籍:10

# Fixed the duplication but still N + 1 problem with the associated books

Author.with_available_books_uniq.size: 2
作者[0]的书籍:10
作者[1]的书籍:10

# Fixed the N + 1 problem but duplicating authors

Author.with_available_books_includes.size: 15

# Fixed the duplication and fixed the N + 1 problem
# BUT now it is filtering out the unavailable books!
# But I want all the Books for these authors!

Author.with_available_books_uniq_includes.size: 2
作者[0]的书籍:10
作者的书籍[1]:5

如何获取所有未重复作者的书籍?我想通过关联对象的属性(书籍上的 available 属性)过滤作者,并且我想预先加载这些书籍。

非常感谢 Chris Oliver 本人回复我关于这种情况的电子邮件查询。

先抓作者:

@uniq_authors_with_available_books = Author.with_available_books_uniq

这正确地抓住了 NeilMixture Author,他们都有可用的书籍:

2.2.1 :004 > @authors_with_available_books.size
=> 2 

但是,如下所示,如果我们想要获取关于这两位作者的书的任何信息,N + 1 问题仍然存在:

2.2.1 :005 > @authors_with_available_books[0].books.size
  (0.2ms)  SELECT COUNT(*) FROM "books" WHERE "books"."author_id" = ?  [["author_id", 1]]
=> 10 

2.2.1 :006 > @authors_with_available_books[1].books.size
  (0.2ms)  SELECT COUNT(*) FROM "books" WHERE "books"."author_id" = ?  [["author_id", 3]]
=> 10

感谢 Chris 的建议。我们要做的是使用 @authors_with_available_books 中的 id 对带有 subquery 的书籍进行单独查询:

2.2.1 :007 > @books_by_those_authors = Book.where(author_id: @authors_with_available_books.map(&:id)) 
  Book Load (0.4ms)  SELECT "books".* FROM "books" WHERE "books"."author_id" IN (1, 3)

从 sql 查询中我们可以看到它只抓取 id 等于 13 的那些书。它只抓取那些作者,因为第一个查询告诉我们只有那些作者才有可用的书。

现在我可以做到了:

@books.size
=> 20 

这是有道理的,因为 Neil 总共有十本书,而 Mixture Author 总共有十本书,总计 20