nemuri
nemuri

Reputation: 203

Rails find record with multiple associated records

Say I have a Teacher model that has_many students through class_listings consisting of teacher_id and student_id:

class Teacher < ActiveRecord::Base
  has_many :class_listings
  has_many :students, through: :class_listings
end

class ClassListing < ActiveRecord::Base
  belongs_to :teacher
  belongs_to :student
end

class Student < ActiveRecord::Base
  has_many :class_listings
  has_many :teachers, through: :class_listings
end

and there exists:

How would I query for a teacher having student 1 and student 2?

I've tried

Teacher.joins(:class_listings).where(class_listings: {student_id: [1,2]})

but that returns teachers with students 1 or 2 (i.e. teacher 1, 2, and 3) as opposed to teachers with students 1 and 2 (i.e. teacher 3).

Upvotes: 1

Views: 1970

Answers (2)

Aleksei Chernenkov
Aleksei Chernenkov

Reputation: 1051

Assuming that your class listings are uniq, you may also do it like that:

student_ids = [1, 2]
Teacher.where(
  id: Rel.select(:teacher_id).group(:teacher_id).where(student_id: student_ids)
    .having("count(student_id) = ?", student_ids.count)
)

SQL is:

SELECT "teachers".* FROM "teachers" WHERE "teachers"."id" IN (
  SELECT "rels"."teacher_id" FROM "rels" WHERE "rels"."student_id" IN (1, 2)
  GROUP BY "rels"."teacher_id" HAVING count(student_id) = 2
)

PS: I use Rel model instead of ClassListing, sorry
PPS: I like @histocrat's solution too.

Upvotes: 4

histocrat
histocrat

Reputation: 2381

This is awkward. Here's one way without writing straight SQL:

Teacher.where(id: ClassListing.where(student_id: 1).select(:teacher_id))
       .where(id: ClassListing.where(student_id: 2).select(:teacher_id))

This is composed of multiple where clauses, but it will only actually make one SQL query, something like Select * from teachers where id in (Select teacher_id from ClassListing where student_id = 1) AND id in (Select teacher_id from ClassListing where student_id = 2).

Upvotes: 4

Related Questions