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

我想创建一个查询链,以便找到给定特定filter的机器人用户。 某些filter可以具有多个值。 例如,我的“locale”filter可以有多个值(例如fr_FR,en_US)。

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

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

SELECT“bot_users”。* FROM“bot_users”WHERE(“bot_users”。“core_bot_id”=?AND(locale =’fr_FR’)或“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[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部分,你能帮帮我吗? 我将数据存储在数据库中并从对象中获取参数。

我和Jaryl在一起。

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

if x? == true if x? == true 。 如果x有问号,那么它已经返回truefalse 。 您不必说, if true == true 。 只是说, if x? 。 比如, if value[0].present? 。 根据您的具体要求,您可以跳过present? 部分,也是。 如果你只是想防止nil值,那么你可以只做if value[0] 。 但是,正如工程师们在评论中指出的那样,如果你想防止空字符串,散列和数组 – 那么你需要坚持使用if value[0].present? 。 请记住,如果你不打算做else ,你总是可以将if语句粘贴在一行的末尾。 喜欢:

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

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

减少循环。 特别是那些迭代变量和值比较( while $i < value.size ) - 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 

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

这是一个看起来像什么的样本,它是不完整的,但我已经为你做了一个很好的测量validation:

 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