Rails使用Ransack搜索integer[]字段报错:operator does not exist
解决Ransack搜索PostgreSQL数组类型字段的问题
这个问题我之前也碰到过!Ransack默认的_in谓词确实不适合PostgreSQL的数组类型,原因很简单——它生成的SQL是用IN来匹配,而PostgreSQL没法直接把integer[]和单个integer做相等比较,所以才会抛出那个运算符不存在的错误。
下面给你两种可行的解决方案,都是通过自定义Ransack谓词来生成正确的SQL:
方案一:使用PostgreSQL数组包含操作符@>
这个方案直接利用PostgreSQL的数组包含特性,检查目标数组是否包含指定的错误码(以单元素数组的形式传入)。
首先在你的InfoData模型中添加自定义Ransack谓词:
class InfoData < ApplicationRecord # 自定义谓词:检查error_codes数组是否包含指定错误码 ransacker :has_error_code, formatter: proc { |val| Arel::Nodes::SqlLiteral.new("ARRAY[#{val}]::integer[]") } do |parent| # 生成SQL:error_codes @> ARRAY[?]::integer[] Arel::Nodes::InfixOperator.new(parent.table[:error_codes], '@>', Arel::Nodes::Placeholder.new) end end
然后在你的搜索表单中,使用这个自定义谓词的_eq后缀:
<%= search_form_for @q do |f| %> <%= f.label :has_error_code_eq, '选择错误码' %> <%= f.select :has_error_code_eq, [9,7,10,21].map { |code| [code, code] } %> <%= f.submit '搜索' %> <% end %>
这样Ransack会生成类似这样的SQL:
SELECT "info_data".* FROM "info_data" WHERE "info_data"."error_codes" @> ARRAY[9]::integer[]
完全符合PostgreSQL数组类型的查询语法,不会再报错。
方案二:使用ANY函数
另一种方式是利用PostgreSQL的ANY函数,检查指定值是否存在于数组中,语法是value = ANY(error_codes)。
同样先在模型中添加自定义谓词:
class InfoData < ApplicationRecord # 自定义谓词:检查错误码是否存在于error_codes数组中 ransacker :error_code_exists do |parent| # 生成SQL:? = ANY(error_codes) Arel::Nodes::Equality.new(Arel::Nodes::Placeholder.new, Arel::Nodes::NamedFunction.new('ANY', [parent.table[:error_codes]])) end end
表单中使用error_code_exists_eq:
<%= search_form_for @q do |f| %> <%= f.label :error_code_exists_eq, '选择错误码' %> <%= f.select :error_code_exists_eq, [9,7,10,21].map { |code| [code, code] } %> <%= f.submit '搜索' %> <% end %>
对应的SQL会是:
SELECT "info_data".* FROM "info_data" WHERE 9 = ANY("info_data"."error_codes")
这种方式同样能正确查询到包含指定错误码的记录。
额外说明
如果需要同时搜索多个错误码(比如同时包含9和7),只需要调整谓词的formatter,让它接受数组参数,生成ARRAY[9,7]::integer[]即可,比如把formatter改成:
formatter: proc { |vals| Arel::Nodes::SqlLiteral.new("ARRAY[#{vals.join(',')}]::integer[]") }
然后表单中传递数组值就能实现多值包含的搜索。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

