如何根据 has_many 关联进行过滤?
How to filter based on has_many association?
我有 3 个模型
项目
class Project < ApplicationRecord
has_many :taggings
has_many :tags, through: :taggings
end
标记
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :project
end
标签
class Tag < ApplicationRecord
has_many :taggings
has_many :projects, through: :taggings
end
简而言之项目有很多标签彻底的标签。
我想找出具有所有给定标签的项目。我的输入是一个 tag
id 的数组(例如 [1,3,5])。我试过
Project.joins(:tags).where(tags: {id: [1 ,3, 5]})
但它会找到具有 ID 来自 [1,3,5]
的任一标签的项目。我正在寻找具有所有输入标签的项目。我该怎么做?
tags = [1, 3, 5]
projects = Project.joins(:tags)
.where(tags: {id: tags})
.group(:id)
.having('COUNT(tags) = ?', tags.size)
这将 return 个具有所有三个标签的项目。
您正在查找“包含”查询:
SELECT p.*
FROM projects p
INNER JOIN taggings t ON p.id = t.project_id
GROUP BY p.id
HAVING array_agg(t.tag_id ORDER BY t.tag_id) @> ARRAY [1, 3, 5];
这将return所有具有所有给定标签但不限于它们的项目。即,如果一个项目有标签 1, 3, 5, 7
,它将被 return 编辑到。但不是
的项目
几个条件:
ARRAY [1, 3, 5]
必须排序
p.id
(实际上是 projects.id
)必须:a) 为主键或 b) 附加唯一性约束。
这样做的好处是查询灵活——可以通过改变运算来快速改变意思。比方说,您现在可以写“return 一个项目 仅 这些标签”,而不是“return 一个项目将所有这些标签”。
考虑这个数据集:
projects:
id name
1 guttenberg
2 x
3 aristotle
tags:
id name
1 books
2 teams
3 management
4 library
5 movie
taggings:
id project_id tag_id
1 1 1
2 1 3
3 1 5
4 2 1
5 2 3
6 3 4
7 3 5
如果您要查询 1, 3
,您应该得到项目 1 和 2。
A SQL fiddle 一起玩:http://sqlfiddle.com/#!17/345dd0/9/1
等效的 ActiveRecord:
tag_ids = [1, 5, 3].sort # condition 1
projects =
Project.joins(:taggings) # don't need tags
.group(:id)
.having("array_agg(taggings.tag_id ORDER BY taggings.tag_id) @> ARRAY[?]", tag_ids)
我有 3 个模型
项目
class Project < ApplicationRecord
has_many :taggings
has_many :tags, through: :taggings
end
标记
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :project
end
标签
class Tag < ApplicationRecord
has_many :taggings
has_many :projects, through: :taggings
end
简而言之项目有很多标签彻底的标签。
我想找出具有所有给定标签的项目。我的输入是一个 tag
id 的数组(例如 [1,3,5])。我试过
Project.joins(:tags).where(tags: {id: [1 ,3, 5]})
但它会找到具有 ID 来自 [1,3,5]
的任一标签的项目。我正在寻找具有所有输入标签的项目。我该怎么做?
tags = [1, 3, 5]
projects = Project.joins(:tags)
.where(tags: {id: tags})
.group(:id)
.having('COUNT(tags) = ?', tags.size)
这将 return 个具有所有三个标签的项目。
您正在查找“包含”查询:
SELECT p.*
FROM projects p
INNER JOIN taggings t ON p.id = t.project_id
GROUP BY p.id
HAVING array_agg(t.tag_id ORDER BY t.tag_id) @> ARRAY [1, 3, 5];
这将return所有具有所有给定标签但不限于它们的项目。即,如果一个项目有标签 1, 3, 5, 7
,它将被 return 编辑到。但不是
几个条件:
ARRAY [1, 3, 5]
必须排序p.id
(实际上是projects.id
)必须:a) 为主键或 b) 附加唯一性约束。
这样做的好处是查询灵活——可以通过改变运算来快速改变意思。比方说,您现在可以写“return 一个项目 仅 这些标签”,而不是“return 一个项目将所有这些标签”。
考虑这个数据集:
projects:
id name
1 guttenberg
2 x
3 aristotle
tags:
id name
1 books
2 teams
3 management
4 library
5 movie
taggings:
id project_id tag_id
1 1 1
2 1 3
3 1 5
4 2 1
5 2 3
6 3 4
7 3 5
如果您要查询 1, 3
,您应该得到项目 1 和 2。
A SQL fiddle 一起玩:http://sqlfiddle.com/#!17/345dd0/9/1
等效的 ActiveRecord:
tag_ids = [1, 5, 3].sort # condition 1
projects =
Project.joins(:taggings) # don't need tags
.group(:id)
.having("array_agg(taggings.tag_id ORDER BY taggings.tag_id) @> ARRAY[?]", tag_ids)