我正在尝试检索具有某些类别的漫画(漫画)。例如,在下面的代码中,我试图搜索冒险(id = 29)和喜剧(id = 25)漫画。我正在使用“ALL”运算符,因为我希望两个类别都在漫画中。 (即通过关系表返回所有同时具有25和29类别的漫画,但也可以附加其他类别)
@search = Manga.find_by_sql("
SELECT m.*
FROM mangas m
JOIN categorizations c ON c.manga_id = m.id AND c.category_id = ALL (array[29,25])
")
有问题吗?查询无法正常工作(可能我误解了所有运算符)。我从查询中得不到任何回报。 所以我试着把它改成
JOIN categorizations c ON c.manga_id = m.id AND c.category_id >= ALL (array[29,25])
我找回了ID大于29的漫画。我甚至没有获得第29类。 我在这里缺少什么吗?
此外,查询......非常慢。如果有人带来一个可以返回我想要的查询,我将不胜感激。
我正在使用Ruby on Rails 4.2和postgresql 感谢
更新(发布模型关系)
class Manga < ActiveRecord::Base
has_many :categorizations, :dependent => :destroy
has_many :categories, through: :categorizations
end
class Category < ActiveRecord::Base
has_many :categorizations, :dependent => :destroy
has_many :mangas, through: :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :manga
belongs_to :category
end
我的尝试基于@Beartech回答:
wheres = categories_array.join(" = ANY (cat_ids) AND ")+" = ANY (cat_ids)"
@m = Manga.find_by_sql("
SELECT mangas.*
FROM
(SELECT manga_id, cat_ids
FROM
(
SELECT c.manga_id, array_agg(c.category_id) cat_ids
FROM categorizations c GROUP BY c.manga_id
)
AS sub_table1 WHERE #{wheres}
)
AS sub_table2
INNER JOIN mangas ON sub_table2.manga_id = mangas.id
")
答案 0 :(得分:1)
我将此作为一个不同的答案添加,因为我喜欢另外一个出于历史原因。它完成了工作,但效率不高,所以也许有人会看到它可以改进的地方。那说......
回答!!!
这一切都回到了Postgresql函数ALL
并不是你想要的。您需要“CONTAINS”运算符,即@>
。您还需要某种聚合函数,因为您希望将每个Manga与其所有类别进行匹配,只选择包含25和29的那些。
这是sql:
SELECT manga.*
FROM
(SELECT manga_id, cat_ids
FROM
(SELECT manga_id, array_agg(category_id) cat_ids
FROM categorizations GROUP BY manga_id)
AS sub_table1 WHERE cat_ids @> ARRAY[25,29] )
AS sub_table2
INNER JOIN manga
ON sub_table2.manga_id = manga.id
;
所以你要拉一个子查询来抓取连接表中的所有匹配行,将它们的类别ID放入一个数组中,然后按漫画ID进行分组。现在你可以加入漫画表来获取实际的漫画记录
红宝石看起来像:
@search = Manga.find_by_sql("SELECT manga.* FROM (SELECT manga_id, cat_ids FROM (SELECT manga_id, array_agg(category_id) cat_ids FROM categorizations GROUP BY manga_id) AS sub_table1 WHERE cat_ids @> ARRAY[25,29] ) AS sub_table2 INNER JOIN manga ON sub_table2.manga_id = manga.id
它快速而干净,在本机SQL中完成所有操作。
您可以将变量插入.find_by_sql()
文本。这为您提供了即时搜索功能,因为@>
询问类别数组是否包含所有搜索词。
terms = [25,29]
q = %Q(SELECT manga.* FROM (SELECT manga_id, cat_ids FROM (SELECT manga_id, array_agg(category_id) cat_ids FROM categorizations GROUP BY manga_id) AS sub_table1 WHERE cat_ids @> ARRAY#{terms} ) AS sub_table2 INNER JOIN manga ON sub_table2.manga_id = manga.id")
Manga.find_by_sql(q)
重要强>
我相当确定上面的代码在某种程度上是不安全的。我假设您将以某种方式验证数组的输入,即
terms.all? {|term| term.is_a? Integer} ? terms : terms = []
答案 1 :(得分:0)
魅力第三次吧?大声笑
好的,完全改变了我的答案,因为看起来这应该在Rails中非常容易,但它已经让我感到难过......
我非常依赖This answer来提出这个问题。你应该在你的漫画模型中放置一个范围:
class Manga < ActiveRecord::Base
has_many :categorizations, :dependent => :destroy
has_many :categories, through: :categorizations
scope :in_categories, lambda { |*search_categories|
joins(:categories).where(:categorizations => { :category_id => search_categories } )
}
end
然后称之为:
@search = Manga.in_categories(25,29).group_by {|manga| ([25,29] & manga.category_ids) == [25,29]}
这将遍历包含两个类别中的至少一个或多个的所有Manga,使用[25,29]
中的数组对manga.category_ids
数组进行“集合”并检查以查看如果该组等于您的请求集。这消除了只有两个键之一的所有漫画。
@search现在将是一个包含两个键true
和false
的哈希:
{true => [#<Manga id: 9, name: 'Guardians of...
.... multiple manga objects that belong to at least
the two categories requested but not eliminated if
they also belong to a third of fourth category ... ]
false => [ ... any Manga that only belong to ONE of the two
categories requested ... ]
}
现在要获得属于BOTH类别的独特Mangas,请使用.uniq:
@search[true].uniq
BOOM !!你有一组Manga对象,它们与你的类别相匹配。
<强> OR 强>
您可以使用以下方式简化它:
@search = Manga.in_categories(25,29).keep_if {|manga| ([25,29] & manga.category_ids) == [25,29]}
@search.uniq!
我更喜欢它,它看起来更干净。
现在为您提供SQL JUNKIES
@search = Manga.find_by_sql("Select *
FROM categorizations
JOIN manga ON categorizations.manga_id = manga.id
WHERE categorizations.cateogry_id IN (25,29)").keep_if {|manga| ([25,29] & manga.category_ids) == [25,29]}
@search.uniq!
*好的确定我会在此之后停止。 :-) *
将它全部放入Manga.rb中的范围:
scope :in_categories, lambda { |*search_categories|
joins(:categories).where(:categorizations => { :category_id => search_categories } ).uniq!.keep_if {|manga| manga.category_ids.include? search_categories[0] and manga.category_ids.include? search_categories[1]} }
有更简单的方法吗? (实际上,最后一个很容易)