繁体   English   中英

如何根据 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)

这将返回具有所有三个标签的项目。

您正在寻找“包含”查询:

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];

这将返回具有所有给定标签但不限于它们的所有项目。 即如果一个项目有标签1, 3, 5, 7 ,它将被返回。 但不是一个项目

几个条件:

  1. ARRAY [1, 3, 5]必须排序
  2. p.id (实际上是projects.id )必须:a) 是主键或 b) 附加一个唯一性约束。

这样做的好处是查询很灵活——您可以更改操作以快速更改含义。 比方说,不是“返回一个包含所有这些标签的项目”,你现在可以写“返回一个只有这些标签的项目”。

考虑这个数据集:

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。

一个可以玩的 SQL 小提琴: 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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM