我意识到仅凭单词来解释我的问题是非常困难的,因此我将使用一个示例来描述我要尝试执行的操作。

因此,例如:

#model Book
has_many: book_genres
has_many: genres, through: :book_genres

#model Genre
has_many: book_genres
has_many: books, through: :book_genres


因此,仅查找属于一种流派的书籍将相对简单,例如:

#method in books model

def self.find_books(genre)
  @g = Genre.where('name LIKE ?' , "#{genre}").take
  @b = @g.books
  #get all the books that are of that genre
end


因此,在Rails控制台中,我可以执行Book.find_books("Fiction"),然后获取所有属于fiction类型的书籍。

但是,我怎么能找到所有既是“年轻人”又是“小说”的书?或者,如果我想查询3种类型的图书,例如“青年”,“小说”和“浪漫”,该怎么办?

我可以做g = Genre.where(name: ["Young Adult", "Fiction", "Romance"]),但是此后我不能做g.books并获得与这3种体裁有关的所有书籍。

我实际上对活动记录非常不好,所以我什至不确定是否有更好的方法直接通过Books查询而不是查找Genre然后查找与其关联的所有书籍。

但是我无法确定的是如何获得所有具有多个(特定)类型的书籍?

更新:

因此,当前提供的Book.joins(:genres).where("genres.name" => ["Young Adult", "Fiction", "Romance"])答案有效,但问题是它会返回所有具有Young AdultFictionRomance风格的书籍。

我要通过什么查询,以使图书返回的图书具有全部3种类型,而不仅仅是3种类型中的1种或2种?

最佳答案

匹配任何给定的流派

以下内容对数组和字符串均适用:

Book.joins(:genres).where("genres.name" => ["Young Adult", "Fiction", "Romance"])
Book.joins(:genres).where("genres.name" => "Young Adult")


通常,最好将哈希传递给where,而不是尝试自己编写SQL代码段。

有关更多详细信息,请参见《 Rails指南》:


http://guides.rubyonrails.org/active_record_querying.html#hash-conditions
http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-the-joined-tables


使用一个查询匹配所有给定类型

可以构建一个查询,然后将其传递给.find_by_query

def self.in_genres(genres)
  sql = genres.
    map { |name| Book.joins(:genres).where("genres.name" => name) }.
    map { |relation| "(#{relation.to_sql})" }.
    join(" INTERSECT ")

  find_by_sql(sql)
end


这意味着调用Book.in_genres(["Young Adult", "Fiction", "Romance"])将运行如下查询:

(SELECT books.* FROM books INNER JOIN … WHERE genres.name = 'Young Adult')
INTERSECT
(SELECT books.* FROM books INNER JOIN … WHERE genres.name = 'Fiction')
INTERSECT
(SELECT books.* FROM books INNER JOIN … WHERE genres.name = 'Romance');


它具有让数据库完成合并结果集的繁重工作的优势。

缺点是我们使用的是原始SQL,因此我们无法将其与其他ActiveRecord方法链接在一起,例如Books.order(:title).in_genres(["Young Adult", "Fiction"])将忽略我们尝试添加的ORDER BY子句。

我们还将SQL查询作为字符串进行处理。有可能我们可以使用Arel避免这种情况,但是Rails和Arel处理绑定查询值的方式使这一过程变得非常复杂。

将所有给定类型与多个查询匹配

也可以使用多个查询:

def self.in_genres(genres)
  ids = genres.
    map { |name| Book.joins(:genres).where("genres.name" => name) }.
    map { |relation| relation.pluck(:id).to_set }.
    inject(:intersection).to_a

  where(id: ids)
end


这意味着调用Book.in_genres(["Young Adult", "Fiction", "Romance"])将运行四个看起来像这样的查询:

SELECT id FROM books INNER JOIN … WHERE genres.name = 'Young Adult';
SELECT id FROM books INNER JOIN … WHERE genres.name = 'Fiction';
SELECT id FROM books INNER JOIN … WHERE genres.name = 'Romance';
SELECT * FROM books WHERE id IN (1, 3, …);


不利的一面是,对于N个类型,我们要进行N + 1个查询。好处是可以将其与其他ActiveRecord方法结合使用。 Books.order(:title).in_genres(["Young Adult", "Fiction"])将进行类型筛选,并按标题排序。

关于ruby-on-rails - Ruby on Rails-有很多,贯穿:找到多个条件,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42455920/

10-11 04:49