Reputation: 335
Say I had 3 tables
users
There is a column in doctor_relationships,
:visited?
a boolean value
What is the best way to Query all of the Users who have visited a certain doctor
I tried
@doctor=doctor.first
doctor.doctor_relationships.where(visited: true)
Which gets me the doctor relationships I want, but then i can't call .users on that active record relation. Is the only way to do it to loop through each relationship in the active record relation and call .user on it, or is there a more ActiveRecord friendly way? ex.
doctor.doctor_relationships.where(visited: true).each do |relationship|
relationship.user
end
Upvotes: 0
Views: 64
Reputation: 5055
In these kinds of situations, I find it useful to invert the relationship in my head. What is the thing you want many of? Start with that model and the query becomes a bit easier to think through.
That gives us:
User.joins(doctor_relationships: :doctor).where(doctor_relationships: {visited: true, doctor: @doctor})
Although that will get you exactly what you're looking for, it's ugly. Let's improve it!
Right now I'm assuming your classes look like this:
class User < ActiveRecord::Base
has_many :doctor_relationships
has_many :doctors, through: :doctor_relationships
end
class DoctorRelationship < ActiveRecord::Base
belongs_to :user
belongs_to :doctor
end
class Doctor < ActiveRecord::Base
has_many :doctor_relationships
has_many :users, through: :doctor_relationships
end
I think it'd be a lot nicer if you could query for this case with something like:
doctor.visits
doctor.patients
To do this, you'll need to modify the models as follows:
class Doctor < ActiveRecord::Base
has_many :doctor_relationships
has_many :visits, -> { where(visited: true) }, class_name: 'DoctorRelationship'
has_many :patients, through: :visits, source: :user
has_many :users, through: :doctor_relationships
end
What we're doing here is telling ActiveRecord that we have an association, visits
, that only exists when the visited
field is true
on DoctorRelationship
. We are then using that association to define another association, patients
, which will only return those users who have actually visited the doctor.
The benefit of this approach (besides readable code) is that it also generates efficient queries:
SELECT "users".* FROM "users" INNER JOIN "doctor_relationships" ON "users"."id" = "doctor_relationships"."user_id" WHERE "doctor_relationships"."doctor_id" = ? AND "doctor_relationships"."visited" = 't'
which happens to be the exact same query that the more verbose ActiveRecord code at the start gave us. Score!
Upvotes: 2
Reputation: 42799
Well start the query from the users model then, i believe something like this should work
@doctor = whatever
User.joins(:doctor_relationships)
.where(doctor_relationships: {visited: true, doctor: @doctor})
Upvotes: 0