Оптимизировать игнорирование неопределенных переменных при построении условий поиска в Rails - PullRequest
0 голосов
/ 28 января 2010

У меня есть метод, который извлекает группы, которые присутствуют в определенных областях. Группам присваиваются значения 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 ]

1 голос
/ 29 января 2010

Звучит как работа для именованных областей:

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? некоторые элементы формы могут отправлять пустые результаты, такие как выборка с чем-то похожим на «Все» по умолчанию.

1 голос
/ 29 января 2010

Вы можете использовать некоторые идиомы 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
1 голос
/ 29 января 2010

Если каждое значение в params является кандидатом на :conditions, вы можете просто сделать это:

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

Это просто выбрасывает значения 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)

Преимущество в том, что вам не нужно добавлять еще одно условие для каждого символа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...