简体   繁体   中英

Rails find where ALL associated records meet condition

I'm trying to get all the clients that have doctors associated BUT none of them has started their first session (one client has_many doctors and can have first sessions with each of them).

So far I have:

@clients =  Client.joins(:doctors).where('doctors.first_session IS NULL').order('clients.id DESC')

But this doesn't work when a client has for example 2 doctors. the first doctor.first_session = null but the second one is not. This case will return the client and it don't want it to.

Any ideas?

This is one of those cases where in order to find records that don't meet a certain condition, you do it by finding all records except those that meet the condition. In SQL this is done with a subquery in the WHERE clause.

For cases like this, the squeel gem is extremely helpful, because it encapsulates the SQL complexity. This is how I would do it (with squeel):

scope :visited_doctor, joins(:doctors).where { doctors.first_visit != nil }
scope :not_visited_doctor, where { id.not_in(Patient.visited_doctor.select(:id)) }

Note that you can do this without squeel, but you'll have to get your hands (and your code) dirty with SQL.

This will work, but may be a little less efficient since it does some of the work in ruby instead of all in the db.

clients = Client.order('clients.id DESC').include(:doctors).select do |client|
  client.doctors.all? {|doctor| doctor.first_session.nil? }
end

Logically, that should fetch all the clients, and then in ruby, the select will evaluate the condition in the block and those clients that return true will be assigned to clients.

The condition block will return true only if all of that client's doctors have a nil first_session.

Hope that helps. There's probably a more efficient way to do this using subselects, but the syntax for that is likely to depend on which database you're using.

Well, I found a solution that involved two queries.

avoid_ids_results = Doctors.select('client_id')
                        .where("first_session IS NOT NULL")
                        .map(&:client_id).join(', ')

@clients =  Clients.
              joins(:doctors).
              where('clients.id NOT IN (' + avoid_ids_results + ')').
              order('clients.id DESC')

Thank you all!

You could create a method in your Client model which returns true if any first_session on a client's doctors is true, something like...

def has_first?
  self.doctors.each do |doctor|
    return true if !doctor.first_session.nil?
  end
  return false
end

This is pseudocode and may need to be tweaked first

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM