这是我为自己找到的解决方案。未经修改,它只能在 Rails 3(或更高版本)中工作。
在标签模型中:
scope :find_by_names, lambda { |names|
unless names.empty?
where("tags.name IN (#{Array.new(names.length, "?").join(",")})", *names)
else
where("false")
end
}
在问题模型中:
scope :tagged_with, lambda { |tag_names|
unless tag_names.blank?
joins(:question_tags).
where("questions.id = question_tags.question_id").
joins(:tags).where("tags.id = question_tags.tag_id").
group("questions.id").
having("count(questions.id) = ?", tag_names.count) & Tag.find_by_names(tag_names)
else
scoped
end
}
结合了两个作用域,& Tag.find_by_names(tag_names)
使得加入tags
实际上是作用域模型上的加入。
[更新]
我的 sql-fu 改进了一点,所以我想我也会提供一个纯 SQL 解决方案:
SELECT q.*
FROM (
SELECT DISTINCT q.*
FROM `questions` q
JOIN question_tags qt
ON qt.question_id = q.id
JOIN tags t
ON t.id = qt.tag_id
WHERE t.name = 'dogs'
) AS q
JOIN question_tags qt
ON qt.question_id = q.id
JOIN tags t
ON t.id = qt.tag_id
WHERE t.name = 'cats'
这会找到所有标记为“猫”和“狗”的问题。这个想法是为我要过滤的每个标签都有一个嵌套的子查询。
还有其他几种方法。我不确定在FROM
子句而不是WHERE
子句中使用子查询是否会有所不同。任何见解将不胜感激。