如何使用 ActiveRecord json 字段类型

Posted

技术标签:

【中文标题】如何使用 ActiveRecord json 字段类型【英文标题】:How to use the ActiveRecord json field type 【发布时间】:2015-01-23 03:11:14 【问题描述】:

我有一个 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 是否每次都会被反序列化?每次我更改部分“哈希”时,该字段是否会被重写(IOW 数据库写入)?

【问题讨论】:

【参考方案1】:

是的,ActiveRecord 允许在其模型中简单地使用 Postgres 的 json-fields 作为哈希。但是,有几点需要考虑:

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

    在 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)

@性能:

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

    是的,每次更改 Hash 中的值时,(整个)JSON 字段都会被替换并更新为新的序列化版本。 对此有两点说明:

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

【讨论】:

【参考方案2】:

进一步澄清 - 当您将 JSON 对象保存到模型实例的属性时确保将其保存为哈希

如果您忘记解析 JSON 字符串,Active Record 不会抱怨:

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

当您从json 属性中检索字符串时,它不会报错而只是返回字符串。

  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 有效负载时遇到了这个问题。无论如何,希望这会有所帮助。

【讨论】:

以上是关于如何使用 ActiveRecord json 字段类型的主要内容,如果未能解决你的问题,请参考以下文章

使用 Activerecord、Rails 和 Postgres 查找具有多个重复字段的行

Mysql 5.7 json 数据类型,在 Rails 5 中使用 activerecord 查询

如何存储和比较:ActiveRecord 中的符号(Ruby on Rails)

如何禁用某个列的 ActiveRecord 日志记录?

如何使用Yii 2 ActiveRecord执行IS NULL和IS NOT NULL?

如何在 Active Record 迁移中为字段添加索引以加快查询速度?