将数组值发送到ruby中的sql查询?

cat*_*045 5 ruby postgresql ruby-on-rails rails-activerecord

我正在努力解决看似红宝石的语义问题.我正在编写一个从表单中获取可变数量的参数并创建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
Run Code Online (Sandbox Code Playgroud)

尽管params_list是一个值与"name LIKE?"相匹配的值数组.在查询中的部分,我收到一个错误:绑定变量的数量错误(1为2):名称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
Run Code Online (Sandbox Code Playgroud)

有人可以说明我应该如何编程吗?

mu *_*ort 5

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

9.23.3.ANY/SOME(数组)

expression operator ANY (array expression)
expression operator SOME (array expression)
Run Code Online (Sandbox Code Playgroud)

右侧是带括号的表达式,它必须产生一个数组值.计算左侧表达式并使用给定运算符与数组的每个元素进行比较,这必须产生布尔结果.ANY如果获得任何真实结果,则结果为"真".如果未找到真实结果,则结果为"假"(包括阵列具有零元素的情况).

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

where name ilike any (array['%Richard%', '%Feynman%'])
Run Code Online (Sandbox Code Playgroud)

这很好,简洁,所以我们如何让Rails构建这个?这实际上非常简单:

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

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

现在你只需要构建names数组.像这样简单的东西应该做:

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

你也可以通过抛出一个并进入混合来转换单个'a b'输入:'a', 'b'splitflatten

names = params.values_at(*fields)
              .select(&:present)
              .map(&:split)
              .flatten
Run Code Online (Sandbox Code Playgroud)