我意识到仅凭单词来解释我的问题是非常困难的,因此我将使用一个示例来描述我要尝试执行的操作。
因此,例如:
#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 Adult
或Fiction
或Romance
风格的书籍。我要通过什么查询,以使图书返回的图书具有全部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/