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
有问号,那么它已经返回true
或false
。 您不必说, 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