在Rails中构建查找条件时优化忽略未定义的变量

时间:2010-01-28 20:58:26

标签: sql ruby-on-rails

我有一个方法可以检索某些区域中存在的组。组被赋予country_id,region_id和city_id

用户界面提供了三个选择框,用于选择国家/地区,该国家/地区以及该地区的城市。要查找特定城市中的所有群组,我有以下代码:

@groups = Group.find(:all, :conditions => {:city_id => params[:city_id]})

这一切都运行正常,但我还希望它在未指定较低条件时查找区域中的所有组。例如,如果给出了一个国家和地区,而不是城市,我想在该地区找到它。

我正在做的是:

if !params[:city_id].nil?
    @groups = Group.find(:all, :conditions => {:city_id => params[:city_id]})
else
    if !params[:region_id].nil?
         @groups = Group.find(:all, :conditions => {:region_id => params[:region_id]})
    else
         @groups = Group.find(:all, :conditions => {:country_id => params[:country_id]})
    end
end

这非常有效,但看起来效率有点低。我这样做是最好的方式还是可以简化一下?

我的一个想法是让一个查找检查所有参数,但我无法弄清楚如何有效地'忽略'参数是零 - 我的主要想法是检查哪些没有设置并将它们设置为类似'*'或'true'的东西,但这不是SQL玩游戏的方式。

3 个答案:

答案 0 :(得分:1)

如果params中的每个值都是:conditions的候选者,您可以这样做:

@groups = Group.all(:conditions => params.reject { |idx, val| val.nil? })

这只是从params中抛出nil值并使用条件的剩余值。

如果您不想使用params中的所有值,则有两种选择。您可以在原始代码中摆脱一堆冗余:

conditions =  if !params[:city_id].nil?
                { :city_id    => params[:city_id] }
              elsif !params[:region_id].nil?
                { :region_id  => params[:region_id] }
              else
                { :country_id => params[:country_id] }
              end

@groups = Group.all(:conditions => conditions)

你可以再敲几行,但它牺牲了一点可读性IMO:

conditions =  if    !params[:city_id].nil?   then { :city_id => params[:city_id] }
              elsif !params[:region_id].nil? then { :region_id => params[:region_id] }
              else { :country_id => params[:country_id] }
              end

或者你可以这样做:

conditions = [:city_id, :region_id, :country_id].inject({}) do |hsh, sym|
  hsh[sym] = params[sym] unless params[sym].nil?
  hsh
end

@groups = Group.all(:conditions => conditions)

这样做的好处是您不需要为每个符号添加另一个条件。

答案 1 :(得分:1)

听起来像命名范围的工作:

class Group < ActiveRecord::Base
  named_scope :in_city, lambda { |city_id| {
    :conditions => { :city_id => city_id }
  }}

  named_scope :in_region, lambda { |region_id | {
    :conditions => { :region_id => region_id }
  }}

  named_scope :in_country, lambda { |country_id | {
    :conditions => { :country_id => country_id }
  }}
end

这为限制组记录建立了一些简单的范围。据推测,您已正确索引数据库,因此可以快速解决这些问题。

控制器更容易实现:

def index
  @group_scope = Group

  if (!params[:city_id].blank?)
    @group_scope = @group_scope.in_city(params[:city_id])
  elsif (!params[:region_id].blank?)
    @group_scope = @group_scope.in_region(params[:region_id])
  elsif (!params[:country_id].blank?)
    @group_scope = @group_scope.in_country(params[:country_id])
  end

  @groups = @group_scope.all
end

通常你应该测试.blank?而不是.nil?因为某些表单元素可以发送空结果,例如选择类似于“全部”的选项作为默认值。

答案 2 :(得分:1)

你可以使用一些Ruby习语来获得更简洁的东西。

尝试这样的事情:(未经测试的代码!)

def index
   @groups = Group.find :all, :conditions => [:city_id, :region_id, :country_id].inject {} do |conditions, name|
      conditions[name] = params[name]  unless params[name].blank?
      conditions
    end
end