I have following models:
# contributor.rb
class Contributor < ApplicationRecord
has_many :skill_groupings
has_many :skills, through: :skill_groupings
end
# skill_grouping.rb
class SkillGrouping < ApplicationRecord
belongs_to :skill
belongs_to :contributor
end
# skill.rb
class Skill < ApplicationRecord
has_many :skill_groupings
has_many :contributors, through: :skill_groupings
end
I want to search (SQL query) for the contributors whose skill_ids contain all the values included in an array.
Suppose data are the following:
contributor_1.skill_ids: [2, 3, 6, 7]
contributor_2.skill_ids: [4, 7]
contributor_3.skill_ids: [9]
contributor_4.skill_ids: []
wanted_ids = [3, 7]
Contributor.joins(:skill_groupings).where(skill_groupings: { skill_id: wanted_ids }).distinct
returns the contributors which have 3 or 7 in their skill ids, hence [contributor_1, contributor_2]
. This is not what I want.
The result of the query should be [contributor_1]
, because only contributor_1
has all the skill ids in wanted_ids
.
Unfortunately, skill_ids
seems to be only an attribute introduced by Rails and not available in SQL.
How can I achieve this only by means of SQL queries?
Assuming that a contributor cannot have the same skill more than once, the simplest way I can see going about this is to find all the contributors with these skills that have the same number of skills as the skills passed in eg
Contributor.joins(:skills)
.where(skills: { id: wanted_ids })
.group(:id)
.having("COUNT(skills.id) = #{wanted_ids.uniq.size}"))
This will result in the following SQL given wanted_ids = [3, 7]
SELECT
contributors.*
FROM
contributors
INNER JOIN skill_groupings ON skill_groupings.contributor_id = contributors.id
INNER JOIN skills ON skills.id = skill_groupings.skill_id
WHERE
skills.id IN (3,7)
GROUP BY
contributors.id
HAVING
COUNT(skills.id) = 2
This will only work for PostgreSQL because this SQL server allows for grouping by a primary key while still selecting the whole row of columns. For other databases you will need a sub subquery such as:
Contributor.where(id:
Contributor.joins(:skills)
.select(:id)
.where(skills: { id: wanted_ids })
.group(:id)
.having("COUNT(skills.id) = #{wanted_ids.uniq.size}")
)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.