Reputation: 636
I have a simple has_many :through arrangement, as shown below
# employee.rb
class Employee < ActiveRecord::Base
has_many :group_assignments
has_many :groups, through: :group_assignments
# ...
end
# group.rb
class Group < ActiveRecord::Base
has_many :group_assignments
has_many :employees, through: :group_assignments
# ...
end
# group_assignment.rb
class GroupAssignment < ActiveRecord::Base
belongs_to :employee
belongs_to :group
end
I have a list of employees. For that list, I want to grab every group that contains at least one of the employees on that list. How would I accomplish this in a manner that isn't horridly inefficient? I'm newish to Rails and very new at SQL, and I'm pretty at a loss. I'm using SQLite in development and PostgreSQL in production.
Upvotes: 0
Views: 113
Reputation: 27374
For a list of employees named employees_list
, this will work:
Group.includes(:employees).where('employees.id' => employees_list.map(&:id))
This is roughly the kind of SQL you will get:
SELECT "groups"."id" AS t0_r0,
"groups"."created_at" AS t0_r1, "groups"."updated_at" AS t0_r2,
"employees"."id" AS t1_r0, "employees"."created_at" AS t1_r1, "employees"."updated_at" AS t1_r2
FROM "groups"
LEFT OUTER JOIN "group_assignments" ON "group_assignments"."group_id" = "groups"."id"
LEFT OUTER JOIN "employees" ON "employees"."id" = "group_assignments"."employee_id"
WHERE "employees"."id" IN (1, 3)
So what is happening is that groups
and group_assignments
tables are first being joined with a left outer join (matching the group_id
column in the group_assignments
table to the id
column in the groups
table), and then employees
again with a left outer join (matching employee_id
in the group_assignments
table to the id
column in the employees
table).
Then after that we're selecting all rows where 'employees'.'id'
(the id of the employee) is in the array of employees in the employee list, which we get by mapping employees_list
to their ids using map
: employees_list.map(&:id)
. The map(&:id)
here is shorthand for: map { |e| e.id }
.
Note that you could use joins
instead of includes
here, but then you would get duplicates if one employee is a member of multiple groups. Kind of subtle but useful thing to know.
Hope that makes sense!
Upvotes: 2
Reputation: 8169
try
Group.joins(:group_assignments).where("group_assignments.employee_id in (?)", @employees.map(&:id))
Upvotes: 0
Reputation: 18080
This is the general idea, but depending on your data, you may need to select distinct.
Group.includes(:group_assignments => :employee).where(:employee => {:id => ?}, @employees.map(&:id))
Upvotes: 0