web-dev-qa-db-ja.com

ActiveRecord jsonフィールドタイプの使用方法

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

すごい!どうすれば使用できますか? フィールドを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データベースの書き込み)。

16
Freedom_Ben

はい、ActiveRecordはPostgresのjson- fieldsを単にモデルのハッシュとして使用することを許可しています。ただし、考慮すべき点がいくつかあります。

  1. 初期化時にハッシュがNULLになる可能性があります
    create_tableの移行では、フィールド:game_boardNULLにすることができます。したがって、最初の使用時には、モデルインスタンスのフィールド:game_boardNULLになり、使用する前にハッシュを初期化する必要があります。 (下の例を参照)

  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フィールドはハッシュ化されてハッシュに変換されます。しかし、それがアプリケーションのパフォーマンスに影響を与えると考える場合は、テキストフィールドを使用してJSON/Hashesをシリアル化/逆シリアル化する必要があるか、またはさらに良いことに、ActiveRecordを使用しないでください。クラスのヒープを作成し、magic-methodsを使用することにより、ActiveRecordは、JSONの逆シリアル化について心配する必要がないほどのオーバーヘッドを作成します。利便性にはコストがかかります。

  2. はい、ハッシュの値を変更するたびに、(全体の)JSONフィールドは新しいシリアル化されたバージョンに置き換えられ、更新されます。
    これに関する2つのメモ:

    • Postgres自体(ActiveRecordだけでなく)でも、特定のJSON要素で更新を実行する可能性は今までありません。 このStackoverflow-questionを比較
    • 一般に、JSONフィールドは固定構造で使用するか、少なくとも管理可能なサイズで使用する必要があります。フィールドタイプは、たとえば次のようなドキュメントストアではありません。 MongoDB。 Postgresのドキュメントを比較
26

さらに明確にするために-JSONオブジェクトをモデルインスタンスの属性に保存する場合必ずハッシュとして保存してください

JSONstringの解析を忘れても、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ペイロードを保存するときにこの問題に遭遇しました。とにかく、これが役に立てば幸いです。

0
Dylan Pierce