ruby-on-rails - 在 Rails 5 中存储 Jsonb 的 Postgres 数组意外转义字符串

标签 ruby-on-rails arrays json postgresql serialization

也许我对这应该如何工作的理解是错误的,但是我看到字符串存储在我的数据库中,而我希望它们是 jsonb array .这是我如何设置:

迁移

t.jsonb :variables, array: true

型号

attribute :variables, :variable, array: true

自定义 ActiveRecord::Type

ActiveRecord::Type.register(:variable, Variable::Type)

自定义变量类型

class Variable::Type < ActiveRecord::Type::Json
  include ActiveModel::Type::Helpers::Mutable

  # Type casts a value from user input (e.g. from a setter). This value may be a string from the form builder, or a ruby object passed to a setter. There is currently no way to differentiate between which source it came from.
  # - value: The raw input, as provided to the attribute setter.
  def cast(value)
    unless value.nil?
      value = Variable.new(value) if !value.kind_of?(Variable)
      value
    end
  end

  # Converts a value from database input to the appropriate ruby type. The return value of this method will be returned from ActiveRecord::AttributeMethods::Read#read_attribute. The default implementation just calls #cast.
  #  - value: The raw input, as provided from the database.
  def deserialize(value)
    unless value.nil?
      value = super if value.kind_of?(String)
      value = Variable.new(value) if value.kind_of?(Hash)
      value
    end
  end

因此,从应用程序的角度来看,此方法确实有效。我可以将值设置为 variables = [Variable.new, Variable.new]它正确地存储在数据库中,并作为 [Variable, Variable] 的数组检索回来.

我所关心的,也是这个问题的根源,是在数据库中,变量是使用双转义字符串而不是 json 对象存储的:
{
  "{\"token\": \"a\", \"value\": 1, \"default_value\": 1}",
  "{\"token\": \"b\", \"value\": 2, \"default_value\": 2}"
}

我希望它们存储的东西更像这样的 json 对象:
{
  {"token": "a", "value": 1, "default_value": 1},
  {"token": "b", "value": 2, "default_value": 2}
}

这样做的原因是,根据我的理解,如果以 json 格式而不是字符串格式直接从数据库查询此列会更快/更容易。通过 rails 查询将不受影响。

如何让我的 Postgres 数据库通过 Rails 正确存储 jsonb 数组?

最佳答案

所以事实证明,Rails 5 attribute api 还不完美(并且没有很好的文档记录),并且 Postgres 数组支持引起了一些问题,至少在我想使用它的方式上是这样。我对解决方案使用了相同的方法来解决问题,但我没有告诉 rails 使用我自定义类型的数组,而是使用自定义类型数组。代码胜于 Eloquent :

迁移

t.jsonb :variables, default: []

型号

attribute :variables, :variable_array, default: []

自定义 ActiveRecord::Type

ActiveRecord::Type.register(:variable_array, VariableArrayType)

自定义变量类型

class VariableArrayType < ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Jsonb

  def deserialize(value)
    value = super # turns raw json string into array of hashes
    if value.kind_of? Array
      value.map {|h| Variable.new(h)} # turns array of hashes into array of Variables
    else
      value
    end
  end

end

现在,正如预期的那样,db 条目不再存储为字符串,而是存储为可搜索/可索引的 jsonb 。这首歌和舞蹈的全部原因是我可以使用普通的旧 ruby​​ 对象设置 variables 属性......

template.variables = [Variable.new(token: "a", default_value: 1), Variable.new(token: "b", default_value: 2)]

...然后将其序列化为数据库中的 jsonb 表示...
[
  {"token": "a", "default_value": 1},
  {"token": "b", "default_value": 2}
]

...但更重要的是,自动反序列化并重新水化回普通的旧 ruby​​ 对象,准备好与它进行交互。

Template.find(123).variables = [#<Variable:0x87654321 token: "a", default_value: 1>, #<Variable:0x12345678 token: "b", default_value: 2>]

使用旧的 serialize api 会导致每次保存时写入(有意通过 Rails 架构设计),无论序列化属性是否已更改。由于可以通过多种方式分配属性,通过覆盖 setter/getter 手动完成所有这些操作是不必要的复杂化,这也是更新 attributes api 的部分原因。

关于ruby-on-rails - 在 Rails 5 中存储 Jsonb 的 Postgres 数组意外转义字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56156323/

相关文章:

javascript - 为什么没有lottie规范?

php - 跨域$.ajax setRequestHeader

ruby-on-rails - OS X 和 Windows 之间的文件权限发生了变化

ruby-on-rails - 什么是可以轻松集成到现有应用程序的优秀 Ruby on Rails 论坛?

ruby-on-rails - 不推荐使用 rails 4 find_by 吗?

javascript - 如何使用继承代码将这个复杂的 json 对象/数组改进为映射

C++ : memory management

java - 使用 Jersey 将传入的 JSON 转换为 Java 数组

ruby-on-rails - Rails文件上载(回形针)编辑

javascript - 显示数组项一次(点击)