【问题标题】:Query Array of Arrays Of Hashes using Postgres使用 Postgres 查询哈希数组
【发布时间】:2016-11-09 18:05:47
【问题描述】:

我有一个模型的列看起来像这样(示例):

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

【问题讨论】:

  • 这个表到底是什么样子的,它的架构是什么? column 是数组列吗? JSONB?还有什么?
  • 我将使用该信息进行编辑
  • @muistooshort 你现在可以看到
  • 您可以更改架构以便不使用serialize 吗? serialize 是一个丑陋的杂物,它只会将一大堆 YAML 放入数据库中,因此您非常不走运,因为在 SQL 中解析 YAML 会非常不愉快。
  • 我很乐意这样做,但使用 array 数据类型我找不到以某种方式构建列的方法,因此我可以这样做 m.column[0][:A]。它显示了一个错误。我需要能够做到这一点。 @muistooshort

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


【解决方案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 的 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_j替换旧的column

    def change
      remove_column :models, :column, :text
      rename_column :models, :column_j, :column
    end
    

当然,您需要在迁移之前备份您的数据库。希望你永远不要再考虑使用serializeserialize 是一个可怕的组合,看起来很简单,但很快就会变成一个针对你的角色扮演游戏。

【讨论】:

  • 您好,谢谢您的回答。实际上,我昨天将数据类型更改为 JSON,现在我正在更改从用户那里收集输入数据以相应地存储它的方式。我正在将嵌套模型更改为那个 JSON 列,看起来更干净。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-24
  • 1970-01-01
  • 2022-11-12
  • 1970-01-01
  • 1970-01-01
  • 2018-10-23
  • 1970-01-01
相关资源
最近更新 更多