使用Postgres

时间:2016-11-09 18:05:47

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

我的模型有一个如下所示的列(示例):

column = [{:A => "1", :B => "2"}, [a, b, c, ..., n]]

abcn也是哈希,如下(示例):

a = {:X => "x", :Y => "y"}

目前,如果我调用模型的记录m,例如m.column[0][:A],则会返回=> "1"
如果我致电m.column[1][0],我会a = {:X => "x", :Y => "y"}

到目前为止,非常好。

现在的问题是,如何获得模型的所有记录的数组,例如column[0][:A] = "1" ???

我尝试这样的事情(但它不起作用):

Model.where("column[0][:A] = ?", "1")

错误说:

PG::SyntaxError: ERROR:  syntax error at or near ":"

修改

columntext数据类型
在其模型中它有serialize :column, Array

1 个答案:

答案 0 :(得分:1)

您的问题是您使用serialize存储数据,serialize只是将大量不可疑的YAML丢入数据库。在PostgreSQL中解析YAML肯定是可能的,但它会很慢而且毫无意义。

如果你真的需要存储一系列哈希值,那么PostgreSQL的jsonb类型将是你最好的选择。您不会在数据库中获得符号键,但是您将获得字符串键,因此这个Ruby数据:

[{:A => "1", :B => "2"}, {:C => 6, :D => 11}]

看起来像这个JSON:

[ { "A": "1", "B": "2" }, { "C": 6, "D": 11 } ]

在数据库内。

在数据库中有jsonb后,您可以使用PostgreSQL提供程序的所有JSON functions and operators查询它,您甚至可以索引JSON以支持更快的查询。在您的情况下,您的查询将如下所示:

Model.where("column->0->>'A' = ?", '1')

RHS上带有整数的->运算符就像Ruby的Array#[]方法一样:

  

-> int
  获取JSON数组元素(从零开始索引,从最后开始计算负整数)

在RHS上使用字符串,它就像Ruby的Hash#[]一样。请注意,->返回JSON,而不是文本或整数。 ->>运算符与->相同,但它会返回文本,因此您最后会使用该运算符来使比较更清晰。

或者你可以说:

Model.where("column->0->'A' = ?", '1'.to_json)

将string-vs-JSON逻辑推送到Ruby中。

一旦你的数据库架构有意义(即使用PostgreSQL jsonb而不是Rails' s serialize),一切都很简单,但你怎么从这里到达那里?首先从模型中删除serialize :column, Array,然后您需要执行以下三步迁移:

  1. 添加jsonb列:

    def change
      add_column :models, :column_j, :jsonb
    end
    
  2. 从数据库中读取每个column值,手动解压缩YAML,然后手动编写JSON:

    def up
      connection.execute('select id, column from models').each do |row|
        a = YAML.load(row['column'])
        connection.raw_connection.exec(
          'update models set column_j = $1 where id = $2',
          [ a.to_json, row['id'].to_i ]
        )
      end
    end
    

    请注意,您不能将此模型用作模型类,并且数据库不再同意models表的结构和格式。

  3. 将旧的column替换为新的column_j

    def change
      remove_column :models, :column, :text
      rename_column :models, :column_j, :column
    end
    
  4. 您希望在迁移之前备份数据库。希望你再也不会考虑再次使用serializeserialize是一个可怕的解决方案,似乎是一个简单的解决方案,但很快变成了一个瞄准你的RPG。