Question

I have a model Planning with a start_time attributes. Say I would like to get all the plannings that are between 9AM and 12PM OR between 6PM and 11PM.

Basically I would do:

Planning.where do 
     (start_time >= @start_time[0]) & (start_time <= @end_time[0]) 
     | 
     (start_time >= @start_time[1]) & (start_time <= @end_time[1])
end

The thing is that the number of time slots varies... Any thougths?

If it can help, I use Squeel gem.

Thanks in advance!

Was it helpful?

Solution

You can do whatever you want within the where block; yet you must return the actual query at the end, because this is what will be used as the where clause.

So, given an array of times like this:

times = [ [ '09:00:00', '12:00:00' ], [ '18:00:00', '23:00:00' ] ]

Here is a verbose solution:

Planning.where do
  query = nil

  times.each do |a,b|
    q = (start_time >= a) & (end_time <= b)

    if query
      query |= q
    else
      query = q
    end
  end

  query
end

Here is a more clever solution:

Planning.where do
  times.map { |a,b| (start_time >= a) & (end_time <= b) }.reduce(&:|)
end

Both do generate the following SQL:

SELECT "plannings".* FROM "plannings"
WHERE ((
  ("plannings"."start_time" >= '09:00:00' AND "plannings"."end_time" <= '12:00:00')
  OR
  ("plannings"."start_time" >= '18:00:00' AND "plannings"."end_time" <= '23:00:00')
))

OTHER TIPS

Can you copy and paste the SQL generated by your ruby code, please ?

EDIT

Ok now I understand your problem and question it was not clear. If you want to keep the code readable you should use ARel instead of squeel in this case (at least not the DSL not made for this). You should be able to apply a map function and then join everything with a OR.

Squeel where() method is returning an AR:Relation, isnt'it ?

You should then be able to chain the where() calls :

finder = Planing.scoped 
time_slots.each do |start_time, end_time|
    finder = finder.where{(start_time >= my{start_time}) & (start_time <= my{end_time}) }
end

I haven't tried this code but I see no reason why it wouldn't work

EDIT : As you stated this will link the conditions with AND , not OR

can you try the following ?

Planning.where do 
    time_slots.inject(false) do |memo, time_slot| 
        memo | (start_time >= time_slot.first) & (start_time <= time_slot.last)
    end
end 

This might be a little too much magick to work with the instance_eval of squeel but give it a try :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top