如何使用 ActiveRecord json 字段类型

How to use the ActiveRecord json field type

我有一个 Rails 模型,它有一个 "json" 类型的数据库列:

create_table "games", force: true do |t|
  t.json     "game_board"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

太棒了!现在我该如何使用它?真的就这么简单吗treating the field like a Hash?

self.game_board[:player1] = 1
self.game_board[:cards] = cards.to_hash

如果我这样写,是否一切都会按预期工作,所以在将来 API 来自客户的电话中我可以这样做吗?:

self.game_board[:player] # And get back the 1 that I put here before

性能如何?即使从未读取过该字段,整个 game_board 每次都会被反序列化吗?每次我更改 "Hash?"

的一部分时,该字段是否会被重写(IOW 数据库写入)

是的,ActiveRecord 允许将 Postgres 的 json-fields 简单地用作其模型中的 Hashes。但是,有几件事需要考虑:

  1. Hash 在初始化时可能为 NULL
    在您的 create_table 迁移中,您允许字段 :game_boardNULL。因此,在第一次使用时,您的模型实例的字段 :game_board 将是 NULL 并且您必须在使用它之前先初始化哈希。 (见下面的例子)

  2. 在JSON中所有键都是字符串
    因此,如果您之前使用过符号或数字,则在保存(和重新加载)时所有键都将转换为字符串。因此,为了防止不需要的行为,建议使用字符串键,除非您的 ORM 配置为符号化所有键。


你的例子:

self.game_board         ||= {}
self.game_board[:player1] = 1
self.game_board[:cards]   = cards.to_hash

# after reload from database (access via String-key):
self.game_board['player1']  # And retrieve value 1 (that we put here before)


@性能:

  1. 是的,每次 ActiveRecord 从数据库中读取条目并创建模型实例时,JSON-字段都会反序列化为哈希。但是,如果您认为这会影响您的应用程序的性能,那么您应该在需要时使用文本字段和 serialize/deserialize JSON/Hashes,或者更好的是,根本不要使用 ActiveRecord .通过创建 类 堆并使用魔术方法,ActiveRecord 产生了如此多的开销,您不必担心 JSON 的反序列化。便利是有代价的。

  2. 是的,每次更改哈希中的值时,(整个)JSON-字段都会被替换并更新为新的序列化版本。
    两个注意事项:

    • 即使在 Postgres 本身(不仅在 ActiveRecord 中),对某些 JSON 元素执行更新的可能性直到现在都是缺失的。 Compare this Whosebug-question
    • 一般来说,JSON-字段应该与固定结构一起使用,或者至少以可管理的大小使用,并且字段类型不应该是文档存储,例如。在 MongoDB。 Compare the Postgres documentation

只是为了进一步说明 - 当您将 JSON 对象保存到模型实例的属性时 确保将其保存为散列

Active Record 不会在您忘记解析 JSON string:

时报错
  game = Game.create(game_board: '"key":"value"')

当您从 json 属性中检索字符串时,它不会抱怨,只会 return 字符串。

  game.game_board
  => '"key":"value"'

因此 game.game_board['key'] 会导致错误,因为您试图将字符串视为哈希。

因此请确保在保存前使用 JSON.parse(string)

  game = Game.create(game_board: JSON.parse('"key":"value"'))

现在你有了预期的行为

game.game_board['key']
=> 'value'

可能对这种情况没有用,但是在从我正在集成的 API 中保存 JSON 有效载荷时遇到了这个问题。无论如何,希望这对您有所帮助。