我编写了一个简单的Rails API,它返回两个日期之间的所有对象。在Rails控制台中,数据库仅查询一次,但API导致对同一请求进行多次查询。
这是控制器方法:
def historic_returns
respond_to do |format|
format.json do
start_date = Date.new(params[:start_year].to_i, params[:start_month].to_i)
end_date = Date.new(params[:end_year].to_i, params[:end_month].to_i)
@result = ShillerDataMonth.records_between_two_dates(start_date, end_date)
render :json => @result
end
end
end
这是控制器使用的模型中的类方法:
def self.records_between_two_dates(start_date, end_date)
ShillerDataMonth.where("record_date >= ? AND record_date <= ?", start_date, end_date).order("record_date asc")
end
当我在Rails控制台中运行ShillerDataMonth#records_between_two_dates方法时,只有一个数据库查询,如预期的那样:
>> ShillerDataMonth.records_between_two_dates(Date.new(2010, 01), Date.new(2012, 01))
ShillerDataMonth Load (2.2ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE (record_date >= '2010-01-01' AND record_date <= '2012-01-01') ORDER BY record_date asc
但是,当我在localhost /historic_returns.json?start_year=2010&start_month=1&end_year=2012&end_month=1上访问此路径时,有多个查询:
Started GET "/historic_returns.json?start_year=2010&start_month=1&end_year=2012&end_month=1" for 127.0.0.1 at 2013-07-08 20:44:34 -0400
Processing by ShillerDataMonthsController#historic_returns as JSON
Parameters: {"start_year"=>"2010", "start_month"=>"1", "end_year"=>"2012", "end_month"=>"1"}
ShillerDataMonth Load (2.1ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE (record_date >= '2010-01-01' AND record_date <= '2012-01-01') ORDER BY record_date asc
ShillerDataMonth Load (0.6ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2009.12' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2009.12' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2009.12' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2009.12' LIMIT 1
ShillerDataMonth Load (0.6ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2010.01' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2010.01' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2010.01' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2010.01' LIMIT 1
ShillerDataMonth Load (0.8ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2010.02' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2010.02' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2010.02' LIMIT 1
CACHE (0.0ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2010.02' LIMIT 1
......每个月都会继续......
为什么API有多个请求?我该如何解决?谢谢你的帮助!
答案 0 :(得分:0)
看起来你遇到了N + 1问题,你可以在这里阅读更多相关信息 - http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
答案 1 :(得分:0)
我在Rails控制台中进行了一些实验,看起来to_json就是为什么要进行这么多不必要的数据库查询的原因。在Rails控制台中,to_json命令是生成所有不必要的查询的地方:
@result = ShillerDataMonth.records_between_two_dates(Date.new(2012, 1), Date.new(2013, 1))
ShillerDataMonth Load (6.5ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE (record_date >= '2012-01-01' AND record_date <= '2013-01-01') ORDER BY record_date asc
>> @result.to_json #multiple queries also happen with the as_json method
ShillerDataMonth Load (3.3ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2011.12' LIMIT 1
ShillerDataMonth Load (0.6ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2011.12' LIMIT 1
ShillerDataMonth Load (0.6ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2011.12' LIMIT 1
ShillerDataMonth Load (0.5ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2011.12' LIMIT 1
ShillerDataMonth Load (0.5ms) SELECT "shiller_data_months".* FROM "shiller_data_months" WHERE "shiller_data_months"."year_month" = '2012.01' LIMIT 1
....
我通过向模型添加方法来修复此问题:
def self.json_formatting(collection)
result = []
collection.each do |shiller_data_month|
result << shiller_data_month.attributes
end
result
end
我在控制器中使用此方法
def historic_returns
respond_to do |format|
format.json do
start_date = Date.new(params[:start_year].to_i, params[:start_month].to_i)
end_date = Date.new(params[:end_year].to_i, params[:end_month].to_i)
@result = ShillerDataMonth.records_between_two_dates(start_date, end_date)
render :json => ShillerDataMonth.json_formatting(@result)
end
end
end
总之,我通过编写自己的方法来解决多个查询的问题,该方法将对象集合转换为哈希集合而不是使用Rails默认值(不确定Rails默认是to_json还是as_json)。