我仍然是Rails的新手,但我会说相当顺利。因此,对于练习,我正在开发一个简单的应用程序,用户可以输入它们的重量,并且使用Lazy Highcharts gem通过Highcharts线图向他们显示30天内的信息。我跟着Ryan Bates的Railscast#223开始了。
我的问题:
好的,所以输入显示除了在用户没有输入值的日子,它在图表上显示为'0'(线条下降到图形的底部),而不是连接到下一个点在任何一天。不确定是否所有这些都是有道理的,所以这是一个截图:
我找到了这个解决方案: Highcharts - Rails Array Includes Empty Date Entries
但是,当我实现该页面上找到的第一个选项时(将0转换为null):
(30.days.ago.to_date..Date.today).map { |date| wt = Weight.pounds_on(date).to_f; wt.zero? ? "null" : wt }
这些点出现但线条没有,工具提示也没有......这让我觉得有些东西打破了js。虽然控制台没有任何明显的错误..?从这里开始,我认为这可能是使用Highchart的'connectNulls'选项的问题,但这也不起作用。实现第二个选项(拒绝方法)时:
(30.days.ago.to_date..Date.today).map { |date| Weight.pounds_on(date).to_f}.reject(&:zero?)
它会从图表中完全删除所有空的日期,这会完全混淆结构,因为这些值应该根据created_at日期显示。
所以回到原点,这就是我剩下的(图表在没有输入的情况下绘制零天数。)
型号:
class Weight < ActiveRecord::Base
attr_accessible :notes, :weight_input
validates :weight_input, presence: true
validates :user_id, presence: true
belongs_to :user
def self.pounds_on(date)
where("date(created_at) = ?", date).pluck(:weight_input).last
end
end
控制器:
def index
@weights = current_user.weights.all
@startdate = 30.days.ago.to_date
@pounds = (30.days.ago.to_date..Date.today).map { |date| Weight.pounds_on(date).to_f }
@h = LazyHighCharts::HighChart.new('graph') do |f|
f.options[:title][:text] = " "
f.options[:chart][:defaultSeriesType] = "area"
f.options[:chart][:inverted] = false
f.options[:chart][:zoomType] = 'x'
f.options[:legend][:layout] = "horizontal"
f.options[:legend][:borderWidth] = "0"
f.series(:pointInterval => 1.day, :pointStart => @startdate, :name => 'Weight (lbs)', :color => "#2cc9c5", :data => @pounds )
f.options[:xAxis] = {:minTickInterval => 1, :type => "datetime", :dateTimeLabelFormats => { day: "%b %e"}, :title => { :text => nil }, :labels => { :enabled => true } }
end
respond_to do |format|
format.html # index.html.erb
format.json { render json: @weights }
end
end
有人有解决方案吗?我想我可能会把这一切都搞错了所以非常感谢任何帮助。
答案 0 :(得分:1)
这是一个HighCharts specfic。您需要将时间戳传入数据阵列,而不是为数据集定义它。
对于每个数据点,我将[time,value]设置为元组
[ "Date.UTC(#{date.year}, #{date.month}, #{date.day})" , Weight.pounds_on(date).to_f ]
您仍然需要以您喜欢的方式删除零,但数据将保留适当的日期值。
我认为你需要删除:pointInterval => 1.day
常规提示
您还应该看看优化您对pound_on的查询,因为您正在对图表上的每个点进行数据库调用。做一个Weight.where(:created_at => date_start..date_end ).group("Date(created_at)").sum(:weight_input)
,它会给你一个created_at日期的数组,每天的总和。
<强> ADDITIONS 强>
改进了SQL查询
这倾向于在sql上做它最擅长的事情。首先,使用何处将查询与您想要的记录相提并论(在本例中为过去30天)。选择所需的字段(created_at和weight_input)。然后启动一个运行sub_query的内部联接,按日对记录进行分组,选择created_at的最大值。当它们匹配时,它会踢回给定日期的最大(也就是最后输入的)重量输入。
@last_weight_per_day = Weight.where(:created_at => 30.days.ago.beginning_of_day..Time.now.end_of_day)
select("weights.created_at , weights.weight_input").
joins("
inner join (
SELECT weights.weight_input, max(weights.created_at) as max_date
FROM weights
GROUP BY weights.weight_input , date(weights.created_at)
) weights_dates on weights.created_at = weights_dates.max_date
")
有了这个,你应该能@last_weight_per_day
这样。假设您已在数据库中验证它们,则不应该有0 / nil值。而且应该很快。
@pounds = @last_weight_per_day.map{|date| date.weight_input.to_f}