【问题标题】:Sending array of values to a sql query in ruby?将值数组发送到 ruby​​ 中的 sql 查询?
【发布时间】:2014-04-23 01:15:22
【问题描述】:

我正在努力解决似乎是 ruby​​ 语义问题。我正在编写一个从表单中获取可变数量的参数并创建Postgresql查询的方法。

def self.search(params)
    counter = 0
    query = ""
    params.each do |key,value|
        if key =~ /^field[0-9]+$/
            query << "name LIKE ? OR "
            counter += 1
        end
    end
    query = query[0..-4] #remove extra OR and spacing from last

    params_list = []
    (1..counter).each do |i|
      field = ""
      field << '"%#{params[:field'
      field << i.to_s
      field << ']}%", '
      params_list << field
    end
    last_item = params_list[-1]
    last_item = last_item[0..-3] #remove trailing comma and spacing
    params_list[-1] = last_item

    if params
        joins(:ingredients).where(query, params_list)
    else
        all
    end
end

尽管 params_list 是一个数值数组,在数量上与“name LIKE?”匹配?在查询部分,我收到一个错误:绑定变量的数量错误(1 对 2)在:name LIKE 中? OR name LIKE ? 我尝试使用 params_list 作为字符串,但也没有更好的效果。 我对红宝石很陌生。

我使用以下代码为 2 个参数工作,但希望允许用户最多提交 5 个 (:field1, :field2, :field3 ...)

def self.search(params)
    if params
        joins(:ingredients).where(['name LIKE ? OR name LIKE ?', 
            "%#{params[:field1]}%", "%#{params[:field2]}%"]).group(:id)
    else
        all
    end
end

有人能解释一下我应该如何编程吗?

【问题讨论】:

  • 您应该发布输入和相关预期输出的示例(即您对query 和params_list 的预期值)。
  • params_list 在这种情况下是一个字符串数组,需要在 sql 语句中识别为单个组件而不是 1 个变量。我希望这是有道理的。

标签: ruby-on-rails ruby postgresql rails-activerecord


【解决方案1】:

您可以轻松实现:

def self.search(string)
  terms = string.split(' ') # split the string on each space
  conditions = terms.map{ |term| "name ILIKE #{sanitize("'%#{term}%'")}" }.join(' OR ')
  
  return self.where(conditions)
end

这应该是灵活的:无论字符串中有多少个术语,它都应该返回匹配至少 1 个术语的对象。

说明:

条件是使用“ILIKE”,而不是“LIKE”:

  • “ILIKE”不区分大小写
  • “LIKE”区分大小写。

sanitize("'%#{term}%'") 部分的用途如下:

  • sanitize() 将防止 SQL 注入,例如放置 ';删除表用户;'作为搜索的输入。

用法:

User.search('Michael Mich Mickey')
# can return
<User: Michael>
<User: Juan-Michael>
<User: Jean michel>
<User: MickeyMouse>

【讨论】:

  • 谢谢!你刚刚给了我一个很棒的迷你课程和一些需要深入研究的东西。
  • 如有任何问题,请随时提出!
  • 您可以使用 ANY 绕过 sanitize 的不愉快。
  • @muistooshort 非常感谢您的提示,我很快就会使用它,我保证!
  • 它对于提高可读性非常方便,它适用于任何比较运算符(即c = any(array[...])),PostgreSQL甚至在内部将c in (...)的值列表形式转换为c = any(array[...])。
【解决方案2】:

PostgreSQL 支持标准 SQL 数组和标准 any op (...) 语法:

9.23.3。任何/某些(数组)

expression operator ANY (array expression)
expression operator SOME (array expression)

右边是一个带括号的表达式,它必须产生一个数组值。左边的表达式使用给定的 operator 计算并与数组的每个元素进行比较,它必须产生一个布尔结果。如果获得任何真结果,ANY 的结果为“真”。如果没有找到真结果(包括数组元素为零的情况),则结果为“假”。

这意味着您可以像这样构建 SQL:

where name ilike any (array['%Richard%', '%Feynman%'])

这很好而且简洁,那么我们如何让 Rails 构建它呢?这其实很简单:

Model.where('name ilike any (array[?])', names.map { |s| "%#{s}%" })

无需手动引用,ActiveRecord 将在填充 ? 占位符时将数组转换为正确引用/转义的列表。

现在您只需构建names 数组。像这样简单的事情应该这样做:

fields = params.keys.select { |k| k.to_s =~ /\Afield\d+\z/ }
names  = params.values_at(*fields).select(&:present)

您还可以通过将split 和flatten 混合在一起,将单个'a b' 输入转换为'a', 'b':

names = params.values_at(*fields)
              .select(&:present)
              .map(&:split)
              .flatten

【讨论】:

    猜你喜欢
    • 2014-10-03
    • 2021-09-28
    • 2019-03-03
    • 1970-01-01
    • 2021-03-08
    • 2012-06-27
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    相关资源
    最近更新 更多