轨。按照数组过滤Postgres对象。错误:格式错误的数组文字

时间:2017-11-06 19:48:16

标签: ruby-on-rails ruby postgresql

无法处理数组过滤。这样:

@profiles = Profile.filter(params.slice(:location, :status, :items))

传递要过滤的记录:

def filter(filtering_params)
  results = self.where(nil)
  puts filtering_params

  filtering_params.each do |key, value|
    if value.is_a?(Array)     
      results = results.public_send(key, value.to_s) if self.where("#{key}", value)
    else
      results = results.public_send(key, value) if value.present? 
    end
  end
  results
end

但Postgres Array存在一些问题。传递给filter方法的参数是:

  

参数:{“utf8”=>“✓”,“location”=>“0”,“status”=>“0”,   “items”=> [“0”,“1”],“commit”=>“Filtruoti”}

并且方法接收它们:

  

{“location”=>“0”,“status”=>“0”,“items”=> [“0”,“1”]}

但我得到错误:

  

PG :: InvalidTextRepresentation:错误:格式错误的数组文字:“0”   第1行:......“= $ 2 AND”个人资料“。”项目“IN('0','1')   ...... ^

     

详细信息:数组值必须以“{”或维度信息开头。 :   SELECT“profiles”。* FROM“profiles”WHERE   “profiles”。“location”= $ 1 AND   “个人资料”。“状态”= $ 2 AND   “profiles”。“items”IN('0','1')限制$ 3 OFFSET $ 4

此错误实际上指向我想要显示收到记录的位置:

<% rofiles.each do |profile| %>

这个Postgres数组发生了什么......?

gem 'rails', '~> 5.0.3'
gem 'pg', '~> 0.18'

编辑:

看起来现在适用范围很好:

scope :items, -> (items) { where "items && ARRAY[?]::integer[]", items } 

def filter(filtering_params)
  results = self.where(nil)

  filtering_params.each do |key, value|
    puts value
    if value.is_a?(Array)     
      value = [0, 1, 2]
      results = results.public_send(key, value) if self.where("key && ARRAY[?]::integer[]", value)
    else
      results = results.public_send(key, value) if value.present? 
    end
  end
  results
end

2 个答案:

答案 0 :(得分:2)

ActiveRecord不会自动为数组类型列创建查询。

查询数组列可以通过以下方式完成:

Profile.where("items@> ARRAY[?]::varchar[]", ["0", "1"])
# or 
Profile.where("items@> ARRAY[?]::integer[]", [0, 1])

但是如果item是您应用程序中的模型,则应该创建一个连接表。数组不是关系数据建模的替代品。

答案 1 :(得分:0)

我使用了不同的方法:

  • 首先,将 Profile.items 设为 JSONB 类型的列。

  • 添加了一个范围:

    scope :has_item, -> (item) { where(%{ items::jsonb ?& array[:item] }, item: item) }
    
  • 最后使用如下:

    Profile.has_item(:item_a)
    
    # or
    
    Profile.has_item([:item_a, :item_b])