Danil
Danil

Reputation: 2537

active record quering in rails 4 and postgres

I have 2 models

Class Ride
  has_many :trips

  #state (string: active or expired)
end

Class Trip
  #date (Date attribute)
  scope :active, -> (start_at = Date.today) { where("trips.date >= ?", [Date.today, start_at].max) }  
end

Daily, I need update state on Rides with active state having all trips with date attribute < Date.today how to perform this in 1 query? i can archive such result using:

Ride.with_active_state.select{|r| r.trips.active.size ==0}

but it makes huje queries to count trips, eq:

    [1] pry(main)> Ride.with_active_state.select{|r| r.trips.active.size ==0}
   (7.3ms)  SELECT f_geometry_column,coord_dimension,srid,type FROM geometry_columns WHERE f_table_name='rides'
  Ride Load (1.6ms)  SELECT "rides".* FROM "rides" WHERE (rides.workflow_state = 'active')
   (2.9ms)  SELECT f_geometry_column,coord_dimension,srid,type FROM geometry_columns WHERE f_table_name='trips'
   (1.3ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 9]]
   (0.7ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 10]]
   (0.7ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 11]]
   (0.7ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 12]]
   (0.8ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 13]]
   (0.8ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 14]]
   (0.5ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 15]]
   (0.5ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 16]]
   (0.5ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 17]]
   (0.5ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 18]]
   (0.5ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 19]]
   (0.5ms)  SELECT COUNT(*) FROM "trips" WHERE "trips"."ride_id" = $1 AND (trips.date >= '2013-09-24')  [["ride_id", 20]]

....

Upvotes: 0

Views: 189

Answers (1)

tihom
tihom

Reputation: 8003

Add scopes on Ride with a group and having clause. It would check the count of all future trips of a ride and return the rides with 0 count.

Class Ride

  scope :active_state, where(state: "active")
  scope :with_nonactive_trips, -> (start_date = Date.today){ joins(:trips).
                                    group("rides.id").
                                    having( ["sum(trips.date > ?) = 0",start_date] ) }

end

Ride.active_state.with_nonactive_trips
# returns All the rides with state == active, alteast one trip and having no trips with date > Date.today

Using a lambda since you had it on the active scope in Trip. I am guessing you need to use a different date than Date.today for some queries.

Upvotes: 2

Related Questions