Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group_by - Ruby/Rails + Postgres

I am pretty new to ROR and Postgre and i'm in trouble to achieve this.

I have a Working_hour Model and a Merchant Model, where merchant has_many working_hours and working_hour belongs to Merchant. The merchant can have two or mores working_hours for the same day.

My view:

 <% @merchant.working_hours.order(:day).group_by(&:day).each do |dia, whs| %>
   <%= t(:"date.abbr_day_names")[dia.to_i] %> : 
     <% whs.each do |wh| %>
       <li>
         <%= wh.oppening_hour.to_formatted_s(:time)  %> -
         <%= wh.close_hour.to_formatted_s(:time)  %>
       </li>
     <% end %>
  <% end %>

When I display at the view ordered by day the data retrieved are (note that the opening hour are unordered):

Mon: 
17:00-20:00
10:00-13:00
Tue:
18:00-21:00 
10:00-13:00

I want to group by day of week and ordering first by day of week and second by opening hour :

Mon: 
10:00-13:00
17:00-20:00
Tue:
10:00-13:00
18:00-21:00 

But as you can see, currently, I'm using the ruby layer to do that what brings performance issues. How can achieve this using the database layer?

like image 376
user1301037 Avatar asked Sep 30 '16 16:09

user1301037


3 Answers

Quick Postgres example if you're willing to store the data in DB table (on randomly created dataset):

-- The query:
SELECT      to_char( mytime, 'day' ) as weekday,                -- example to get weekday name
            extract( dow from mytime ) as weekday_num,          -- example to get weekday number
            format(                                             -- format the way example output was given
                '%s - %s',
                date_trunc( 'hour', opening_time )::time(0),    -- get opening hour (without milliseconds)
                date_trunc( 'hour', closing_time )::time(0)     -- get closing hour (without milliseconds)
            ) as working_hours
FROM        mytable
GROUP BY    mytime,         -- to secure accurate ordering by timestamp
            weekday,        
            working_hours
ORDER BY    mytime,
            working_hours;

-- Result:
  weekday  | weekday_num |    working_hours
-----------+-------------+---------------------
 monday    |           1 | 08:00:00 - 17:00:00
 tuesday   |           2 | 08:00:00 - 16:00:00
 tuesday   |           2 | 08:00:00 - 17:00:00
 wednesday |           3 | 08:00:00 - 12:00:00
 thursday  |           4 | 08:00:00 - 12:00:00
 thursday  |           4 | 08:00:00 - 16:00:00
 friday    |           5 | 08:00:00 - 15:00:00
 friday    |           5 | 08:00:00 - 18:00:00

Postgres documentation links that might come handy:

https://www.postgresql.org/docs/current/static/functions-datetime.html https://www.postgresql.org/docs/current/static/functions-formatting.html https://www.postgresql.org/docs/current/static/functions-string.html#FUNCTIONS-STRING-FORMAT

P.S. Hopefully gives some ideas how to solve it in database.

like image 75
Kristo Mägi Avatar answered Oct 17 '22 08:10

Kristo Mägi


The working hour should be ordered by opening_hour as you will show the opening hour in UI in ascending order. Once the working hour is ordered, the result can be grouped by day.

<% @merchant.working_hours.order(:opening_hour).group_by(&:day).each do |dia, whs| %> <%= t(:"date.abbr_day_names")[dia.to_i] %> : <% whs.each do |wh| %> <li> <%= wh.opening_hour.to_formatted_s(:time) %> - <%= wh.close_hour.to_formatted_s(:time) %> </li> <% end %> <% end %>

like image 42
Minu Avatar answered Oct 17 '22 07:10

Minu


<% day = nil %>
<% @merchant.working_hours.order(:day, :oppening_hour).each do |wh| %>
  <% if wh.day != day %>
    <% day = wh.day %>
    <%= t(:"date.abbr_day_names")[day.to_i] %> :
  <% end %>
  <li>
    <%= wh.oppening_hour.to_formatted_s(:time)  %> -
    <%= wh.close_hour.to_formatted_s(:time)  %>
  </li>
<% end %>
like image 20
Alex Avoiants Avatar answered Oct 17 '22 07:10

Alex Avoiants