Rails Ransack搜索PG hstore

时间:2014-08-20 19:41:18

标签: ruby-on-rails ransack

我在Rails4应用中使用Ransack。我最近在表中添加了一个PG hstore字段。在此应用程序中,用户可以定义hstore字段内容(键和值)。

如果我知道密钥名称,我找到了一种使用Ransack搜索hstore字段的方法。这是一个示例 - 关键是color

ransacker :color do |parent|
  Arel::Nodes::InfixOperation.new('->', parent.table[:data], 'color')
end

然后在视图中,我使用:

    <td><%= f.label :color_cont, "Color:" %></td>
    <td><%= f.text_field :color_cont %></td>

但是,我不知道用户将使用key

有没有人想办法让Ransack在不知道密钥的情况下搜索hstore字段?

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

您可以直接使用Arel :: Nodes执行此操作,但您也可以使用在Ransack的更高版本中添加的ransackable_scopes方法。

首先创建一个返回hstore_col_name的方法。

然后创建一些使用hstore列上的postgres运算符的作用域。第一个范围可用于查找具有key_value_pair的行。第二个范围可用于查找具有给定键和任何值的行,这可能也会有所帮助。

scope :with_key_value_pair, ->(key,value){where("#{table_name}.#{hstore_col_name} @> '#{key}=>#{value}'")}
scope :with_key, ->(key){where("#{table_name}.#{hstore_col_name} ? :key", :key=>key)}

然后创建一个类方法,该方法将动态返回该hstore列中使用的所有当前唯一键的数组:

def self.hstore_keys_to_a
  self.connection.execute("SELECT DISTINCT UNNEST(AKEYS(#{hstore_col_name})) from #{table_name}").map(&:values).flatten.uniq
end

由于我没有找到在一个范围内使用带有两个参数的ransack的任何示例,我创建了另一个类方法来动态添加范围以便调用ransack,匹配当前在hstore列中使用的每个键。也许有一种更直接的方法来传递两个参数,而不是将一个参数放入范围名称中,因为我已经在这里完成了:

def self.add_hstore_key_scopes do
  hstore_keys_to_a.each do |key|
    define_singleton_method "with_#{key}" do |val|
      with_key_value_pair(key,val)
    end
  end
end

def self.ransackable_scopes(auth_obj=nil)
  hstore_keys_to_a.map{|key| "with_#{key}"} << "with_key"
end

我添加了静态&#39; with_key&#39;范围为ransackable_scopes,允许选择搜索具有给定键和任何值的行。

然后在搜索表单的rails控制器方法中,在渲染之前调用Model.add_hstore_key_scopes。这将确保最近添加的任何键都添加了_#{key}范围。

现在,在视图中,您可以为正在使用的每个当前键呈现部分:

<% Model.hstore_keys_to_a.each do |key| %>
  <%= f.label "with_#{key}" %>
  <%= f.text_field "with_#{key}" %>
<% end %>

如果您愿意,可以使用另一个字段来搜索具有任意值的给定键的行:

<%= f.label "with_key" %>
<%= f.text_field "with_key" %>

答案 1 :(得分:0)

我最终在车型中做到了这一点:

if ActsAsTenant.current_tenant.data.present?
  ActsAsTenant.current_tenant.data.each do |key, value|
    ransacker key do |parent|
      Arel::Nodes::InfixOperation.new('->', parent.table[:data], key)
    end
  end
end

这在视图中:

      <% if current_tenant.data.present? %>
          <% current_tenant.data.each do |key, value| %>
              <td><%= f.label key + '_cont', key.capitalize + ":" %></td>
              <td><%= f.text_field key + '_cont' %></td>
          <% end %>
      <% end %>
相关问题