0

I'm struggling with understanding how group works in Rails. There doesn't really appear to be any good tutorials either...

class Doctor
  has_many :appointments
  has_many :patients, through: :appointments
end

class Appointment
  has_many :doctors
  has_many :patients
end

class Patient
  has_many :appointments
  has_many :doctors, through: :appointments
end

The Doctor class has a field primary_doctor. A patient can have many doctors, but only one primary_doctor.

Given a specific doctor, I want a list of all patients that doctor sees, grouped by the primary_doctor for each patient.

doctor.patients.joins(:appointments).where(appointments: { is_primary: true }).group("patients.id, appointments.doctor_id")

is what I feel should work, but that doesn't do any grouping. If I add a .count to the end, it almost gives me what I want, but instead of the actual objects, I get a hash of {doctor_id=>patient_count}.

Thoughts? Thanks!

1
  • I think you need to select the fields you need after grouping. Commented Apr 28, 2016 at 14:40

1 Answer 1

3

If I understand your question correctly, you need to be using Ruby's in-memory group_by function. Unless I've missed something the last 10 years, ActiveRecord can't marshal a database query straight into the type of representation you're looking for.

So, to get a list of all patients that doctor sees, grouped by the primary_doctor for each patient, you could do:

doctor.patients.joins(:appointments).where(appointments: { is_primary: true }).
  group_by(&:primary_doctor)

This would give you a result like:

{
  <Doctor id: 1, name: "Dr Bob"> =>
    [<Patient id: 1, name: "Joe">,
     <Patient id: 2, name: "Jane">],
  <Doctor id: 2, name: "Dr Spock"> =>
    [<Patient id: 3, name: "Jack">,
     <Patient id: 4, name: "Jill">,
     <Patient id: 5, name: "Scotty">]
}

Note, this might be mildly inefficient if you have to go back to the database each time to get the primary_doctor, so if this is a critical path in your app you will probably also use includes (http://apidock.com/rails/ActiveRecord/QueryMethods/includes) somewhere in there.

Sign up to request clarification or add additional context in comments.

5 Comments

Are you sure about this? The API documentation says its quite possible to get a collection of records. api.rubyonrails.org/classes/ActiveRecord/…
The collection returned by AR is a single-dimensional array, even in the documentation you linked to, except sometimes in some edge cases like count. You stated you'd like a map (so in Ruby, a hash structure) of primary doctor to an array of patients. SQL group is for compiling many rows into a single representation before marshalling; that's not what you asked for here, at least.
Added sample output to my answer so you can see how it's different than the AR documentation and whether it's what you meant.
Beautiful, group_by is what I need. So group is really only used for aggregate functions (sum, avg, etc)?
Personally, yes. I recently used it as part of a query to find duplicate records, as another example, but in my experience it's extremely uncommon in standard CRUD operations (as your example seems to be a part of).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.