2011-10-10 78 views
1

我使用的是Ruby on Rails 3,我有一个存储check_in和check_out datetime的“访问”模型,我需要在一般日期范围内通过访问进行搜索并统计当天所有时段分组的“在场观众数”。Ruby on Rails 3按小时检入/检出范围

......即,我需要类似的东西:

 
8:00am - 8:59am : 12 visitors 
9:00am - 9:59am : 5 visitors 
10:00am - 10:59am : 4 visitors 

...给出一个访问表与检查和检出时间存储。

这个想法是采取“访问”的入住和退房时间,然后确定在一天中的任何给定时间内有多少访问者(假设每次访问记录一位访问者,由政策执行)访问的时间为了找出高峰访问时间。

我试图建立查询:

eight_am_visits = Visit.where("EXTRACT(HOUR_MINUTE FROM check_in) <= 859").where("EXTRACT(HOUR_MINUTE FROM check_out) >= 800") 

...并没有它,因为Rails的专卖店在这样一个奇怪的时尚日期相当命中(UTC中,它会在数据库转换查询),并且当我在SQL中使用像EXTRACT这样的东西时,它似乎没有进行这种转换...

...任何想法如何做到这一点?

回答

0

感谢您的帮助Olexandr和mu,我设法弄清楚您在这里给我的见解。

我想出了这一点,似乎工作:

#grab the data here, this is nice because 
#I can get other stats out of it (which I don't show here) 
@visits = Visit.where(:check_in => @[email protected]_date, :check_out => @[email protected]_date).where("check_out IS NOT NULL"); 

#Here we go 
@visitors_present_by_hour = {} 
(0..23).each do |h| 
    # o.o Ooooooh.... o_o Hee-hee! ^_^ 
    @visitors_present_by_hour[h] = @visits.collect{|v| v.id if v.check_in.hour <= h and v.check_out.hour >= h}.compact.count 
end 

然后我可以只转储出该散列在我看来。

看来解决方案比我想象的要简单一些,而且这样做实际上会让rails从UTC时间转换。

所以,我可以收集所有在小时范围内有几个小时的访问,然后压缩出nils并计算剩余的时间。一旦我碰到它,我很惊讶。我根本不需要任何自定义SQL(除非这是完全错误的,但它似乎与某些测试数据一起工作)。

谢谢你们!

+0

...这里的一个注意事项是,这不包括跨越多天的访问,虽然这对于我的数据并不重要,因为这从来不会因政策而发生。如果你需要这个跨越多天,你可能需要收集一些更聪明的条件... – ZroonStack

1

也许这样的事?

t = Time.now 
eight_am_visits = Visit.all(:conditions => ['check_in > ? and check_in < ?', Time.utc(t.year, t.month, t.day, 8), Time.utc(t.year, t.month, t.day, 8, 59)]) 

编辑: 或者你可以抓住白天的所有访问,并在Rails的过滤它:

t = Time.now 
visits = Visit.all(:conditions => ['created_at > ? and created_at < ?', Time.utc(t.year, t.month, t.day - 1), Time.utc(t.year, t.month, t.day + 1)]) 
visits_by_hour = [] 
(0..23).each do |h| 
    visits_by_hour << visits.map {|e| e if e.created_at > Time.utc(t.year, t.month, t.day, h) && e.created_at < Time.utc(t.year, t.month, t.day, h, 59)}.count 
end 

,并考虑:

<% visits_by_hour.each_with_index do |h, v| %> 
    <%= "#{h}:00 - #{h}:59: #{v} visitors" %> 
<% end %> 
+0

很酷,但这是否意味着我可以使用Rails功能来管理这种方法的唯一方法是在期望的日期范围内每天循环并为每一天执行查询? – ZroonStack

+0

我觉得这里有点棘手的是,我必须循环遍历范围内的每一天来更新时间值。我很想尝试你的方法的一个变体,但是使用'Time.new(1970,1,1,h).utc.strftime(“%l%M”)。strip'这样的东西来获得一个通用的hour_minute字符串在正确的时区,用于与EXTRACT(HOUR_MINUTE FROM check_in)等比较。我开始看到,可能没有更好的方式比循环每个小时,就像你在这里... o_O – ZroonStack

1

看起来你不是实际上对Visit对象感兴趣。如果你只是想要一个简单的总结,然后推AR闪开,让数据库做的工作:

# In visit.rb 
def self.check_in_summary(date) 
    connection.select_rows(%Q{ 
     select extract(hour from check_in), count(*) 
     from visits 
     where cast(check_in as date) = '#{date.iso8601}' 
     group by extract(hour from check_in) 
    }).inject([ ]) do |a, r| 
     a << { :hour => r[0].to_i, :n => r[1].to_i } 
    end 
end 

然后a = Visit.check_in_summary(Date.today - 1)会给你昨天的总结无需做任何额外的工作。该演示实施,当然,对小时阵列中的孔而没有任何签但这是容易解决的(如果需要):

def self.check_in_summary(date) 
    connection.select_rows(%Q{ 
     select extract(hour from check_in), count(*) 
     from visits 
     where cast(check_in as date) = '#{date.iso8601}' 
     group by extract(hour from check_in) 
    }).each_with_object([0]*24) do |r, a| # Don't forget the arg order change! 
     a[r[0].to_i] = r[1].to_i 
    end 
end 

该版本返回具有24个元件为每个零的阵列(一个以小时为单位),其值是该小时内的签到次数。

不用害怕在方便的时候下降到SQL,AREL只是一个工具,你应该在你的工具箱中有多个工具。另外,不要害怕向模型添加额外的数据整理和总结方法,您的模型应该有一个界面,可以让您在其余代码中清晰地表达您的意图。

+0

请问这个考虑到Rails存储UTC时间?但是,这给了我一些很好的见解,但是,您的代码给我的是“每小时登记入住”而不是“任何给定时间的当前访客数量”。我可以看到如何在这一点上对日期范围进行一些额外的过滤。 – ZroonStack

+0

@ZononStack:你可以将'check_in'时间转换为数据库中你需要的时区。您可以像上面那样计算签入和签出,然后减去(在数据库内部或外部)以获得最终结果。 –