Rails,其中查询链带有或用于数组输入

时间:2017-09-29 12:33:14

标签: ruby-on-rails ruby postgresql

我想创建一个查询链,以便在给定特定过滤器的情况下找到我的机器人用户。某些过滤器可以包含多个值。例如,我的#34; locale"过滤器可以有多个值(例如fr_FR,en_US)。

在此示例中,我检查了两个区域设置复选框(fr_FR和en_US)。

我创建了一个查询链,但输出不是我想要的:

  

SELECT" bot_users"。* FROM" bot_users" WHERE(" bot_users"。" core_bot_id"   =? AND(locale =' fr_FR')OR" bot_users"。" core_bot_id" =? AND(locale =' fr_FR')AND(locale =' en_EN'))[[" core_bot_id",1],   [" core_bot_id",1]]

我想要这样的事情:

  

SELECT" bot_users"。* FROM" bot_users" WHERE(" bot_users"。" core_bot_id"   =? AND(locale =' fr_FR' OR' en_EN'))[[" core_bot_id",1]]

以下是代码:

@filter = Filter.find_by_letter_id(@letter.id)
        $i = 1
        $a = 1
        $s = 1
        query = [{first_name: @filter.first_name}, {last_name: @filter.last_name}, {source: @filter.segment}, {gender: @filter.gender}, {timezone: @filter.timezone}, {locale: @filter.locale}, {created_at: [@filter.creation_date_start, @filter.creation_date_finish]}]
        query_chain = BotUser.where(core_bot_id: @bot.id)

          query.each do |hash|
            hash.each_pair do |key, value|
              if value.present? == true
                  if key.to_s == "timezone"
                    while  $i < value.size do
                      query_chain = query_chain.where("timezone = ?", value[$i].to_f)
                      $i += 1
                    end
                  elsif key.to_s == "locale"
                    while  $a < value.size do
                      puts $a.to_s
                      if $a == 1
                        query_chain = query_chain.where("locale = ?", value[$a])
                      else
                        query_chain = query_chain.or(query_chain.where("locale = ?", value[$a]))
                      end
                      $a += 1
                    end
                  elsif key.to_s == "gender"
                      query_chain = query_chain.where("gender = ?", value)
                  elsif key.to_s == "core_bot_id"
                      query_chain = query_chain.where("core_bot_id = ?", value)
                  elsif key.to_s == "created_at"
                    if value[0].present? == true and value[1].present? == true
                      query_chain = query_chain.where('created_at BETWEEN ? AND ?', value[0], value[1].end_of_day)
                    elsif value[0].present? == true
                      query_chain = query_chain.where('created_at > ?', value[0])
                    elsif value[1].present? == true
                      query_chain = query_chain.where('created_at < ?', value[1].end_of_day)
                    end
                  else
                    query_chain = query_chain.where("#{key} = ?", value)
                  end
                end
              end
          end

更新,尝试Jaril方法:

控制器:

private

    def filter_params
      params.fetch(:query, {}).permit(:first_name, :last_name, :timezone, :gender)
    end

    def set_nb_recipients
    @filter = Filter.find_by_letter_id(@letter.id)


filter_params = ActionController::Parameters.new({
 query: {
        core_bot_id: @bot.id,
        first_name:  @filter.first_name,
        last_name: @filter.last_name,
        source: @filter.segment,
        gender: @filter.gender,
        timezone: @filter.timezone,
        locale: @filter.locale,
        creation_date_start: @filter.creation_date_start,
        creation_date_finish: @filter.creation_date_finish
      }
    })
    query = FilterQuery.new(filter_params)

            query = FilterQuery.new(filter_params)
            @bot_users = query.execute || BotUser.none

            @nb_users = @bot_users.length
     end

应用程序/模型/ filter_query.rb

class FilterQuery

  include ActiveModel::Model

  attr_accessor :first_name, :last_name, :timezone, :gender, :locale, :core_bot_id, :source, :creation_date_start, :creation_date_finish

  validates :gender, inclusion: { in: %w(male female) }

  def initialize(params)
    super(params)
  end

  def execute
    return false unless valid?
    @bot_users = BotUser.where(core_bot_id: core_bot_id)
    @bot_users = @bot_users.where('first_name LIKE ?', "#{first_name}%") if first_name.present?
    @bot_users = @bot_users.where('last_name LIKE ?', "#{last_name}%") if last_name.present?
    @bot_users = @bot_users.where(timezone: timezone) if timezone.present?
    @bot_users = @bot_users.where(timezone: locale) if locale.present?
    @bot_users = @bot_users.where(gender: gender) if gender.present?
    @bot_users = @bot_users.where(source: source) if source.present?
    @bot_users = @bot_users.where('created_at BETWEEN ? AND ?', creation_date_start, creation_date_finish) if creation_date_start.present? and creation_date_finish.present?
    @bot_users = @bot_users.where('created_at > ?', creation_date_start) if creation_date_start.present? and creation_date_finish.present? == false
    @bot_users = @bot_users.where('created_at < ?', creation_date_finish) if creation_date_start.present? == false and creation_date_finish.present?
    @bot_users
  end

end

不幸的是,这不会返回任何东西。我不确定params部分,你能帮帮我吗?我将数据存储在数据库中并从对象中获取参数。

2 个答案:

答案 0 :(得分:3)

理想情况下,您希望在查询类中执行所有这些操作。另外,试着调低if / else是的吗?

以下是一个样本的样本,它不完整,但是我已经为你做了一个很好的测量验证:

class FilterQuery

  include ActiveModel::Model

  attr_accessor :first_name, :last_name, :timezone, :gender

  validates :gender, inclusion: { in: %w(male female) }

  def initialize(params)
    super(params)
  end

  def execute
    return false unless valid?
    @bot_users = BotUser.all
    @bot_users = @bot_users.where('first_name LIKE ?', "#{first_name}%") if first_name.present?
    @bot_users = @bot_users.where('last_name LIKE ?', "#{last_name}%") if last_name.present?
    @bot_users = @bot_users.where(timezone: timezone) if timezone.present?
    @bot_users = @bot_users.where(gender: gender) if gender.present?
    @bot_users
  end

end

要使用它,请将其放入您的控制器:

def index
  query = FilterQuery.new(filter_params)
  @bot_users = query.execute || BotUser.none
end

private

def filter_params
  params.fetch(:query, {}).permit(:first_name, :last_name, :timezone, :gender)
end

答案 1 :(得分:3)

我和Jaryl在一起。

减少if/elsif。您正在寻找的是case。由于您的一堆查询具有相似的结构,因此您可以将它们填充到else语句的case子句中。

减少if x? == true。如果x有问号,那么它已经返回truefalse。你不必说if true == true。简单地说,if x?。就像,if value[0].present?。根据您的具体要求,可能也可以跳过present?部分。如果您只是想防范nil值,那么您可以if value[0]。但是,正如工程师在评论中指出的那样,如果你想防止空字符串,散列和数组 - 那么你需要坚持if value[0].present?。请记住,如果您不打算if,则可以始终将else语句放在一行的末尾。像:

query_chain = query_chain.where('created_at > ?', value[0]) if value[0].present?

减少类型转换(key.to_s)。只需将密钥变量与另一个密钥进行比较。为什么要将它转换为字符串?

减少循环。特别是那些迭代变量和值比较(while $i < value.size) - 令人讨厌!这样:

while  $i < value.size do
  query_chain = query_chain.where("timezone = ?", value[$i].to_f)
  $i += 1
end

不是惯用语。更好的是:

value.each do |timezone|
  query_chain = query_chain.where("timezone = ?", timezone.to_f)
end

当然,您可以使查询更简洁:

value.each do |timezone|
  query_chain = query_chain.where(timezone: timezone.to_f)
end

但是,你所做的就是每个循环都将时区转换为_f。那么,为什么不一次性做到这一点并链接一个查询,如:

timezones = value.map{|timezone| timezone.to_f}
query_chain = query_chain.where(timezone: timezones)    

当然,你可以保存自己的临时变量赋值,然后执行:

query_chain = query_chain.where(timezone: value.map{|timezone| timezone.to_f})

如果你不介意长(ish)行。

我喜欢Jaryl的方法。但是,如果你想坚持当前的方法,它可能看起来像:

query.each do |hash|
  hash.each_pair do |key, value|
    if value
      case key
      when :timezone
        query_chain = query_chain.where(timezone: value.map{|timezone| timezone.to_f})
      when :created_at
        query_chain = query_chain.where('created_at > ?', value[0]) if value[0]
        query_chain = query_chain.where('created_at < ?', value[1].end_of_day) if value[1]
      else
        query_chain = query_chain.where(key => value)
      end
    end
  end
end

这里的Jaryl方法略有不同......

class FilterQuery

  attr_accessor :first_name, 
                :last_name, 
                :timezone, 
                :gender, 
                :locale, 
                :core_bot_id, 
                :source, 
                :creation_date_start, 
                :creation_date_finish

    def initialize(params)
      params.each{|k,v| send("#{k}=",v)}
    end

    def execute
      return false unless valid?
      @bot_users = BotUser.where(core_bot_id: core_bot_id)
      [:first_name, :last_name].each do |var_sym|
        val = send(var_sym)
        @bot_users = @bot_users.where("#{var_sym} LIKE ?", "#{val}%") if val.present?
      end
      [:timezone, :locale, :gender, :source].each do |var_sym|
        val = send(var_sym)
        @bot_users = @bot_users.where(var_sym => val) if val.present?
      end
      @bot_users = @bot_users.where('created_at > ?', creation_date_start) if creation_date_start.present?
      @bot_users = @bot_users.where('created_at < ?', creation_date_finish) if creation_date_finish.present?
      @bot_users
    end

  private

    def valid?
      %w(male female).include? gender
    end

end