Freedom_Ben Freedom_Ben - 1 year ago 51
JSON Question

How to use the ActiveRecord json field type

I have a Rails model which has a database column of type "json":

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

Great! Now how do I use it? Is it really just as simple as treating the field like a

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

If I were to write that, would everything just work as expected, so in a future API call from a client I could do this?:

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

What about performance as well? Will the entire
be de-serialized every time even if that field is never read? Will the field be re-written (IOW a database write) each time I change part of the "Hash?"

Answer Source

Yes, ActiveRecord allows to use Postgres' json-fields simply as Hashes in their models. However, there are a couple of things to consider:

  1. Hash may be NULL on initialization
    In your create_table migration you allow the field :game_board to be NULL. Thus, on first usage the field :game_board of your model-instance will be NULL and you have to initialize the Hash first before using it. (See example below)

  2. In JSON all keys are Strings
    Thus, on save (and reload) all keys will be transformed into Strings if you have used Symbols or Numbers before. Thus, to prevent unwanted behavior it is recommended to use String-keys unless your ORM is configured to symbolize all keys.

Your examples:

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)

@ Performance:

  1. Yes, every time ActiveRecord reads an entry from the database and creates a model-instance, JSON-fields get unserialized into Hashes. But if you think that is a performance-hit to your application than you should either use a text-field and serialize/deserialize the JSON/Hashes when you need to or, even better, don't use ActiveRecord at all. By creating heaps of classes and using magic-methods, ActiveRecord creates so much overhead that you shouldn't worry about the deserialization of JSON. Convenience has its costs.

  2. Yes, every time you change the a value in the Hash, the (whole) JSON-field gets replaced and updated with the new serialized version.
    Two notes on this:

    • Even in Postgres itself the possibility of perform updates on certain JSON-elements is missing until now. Compare this Stackoverflow-question
    • In general, JSON-fields should be used with a fixed structure or, at least, in manageable sizes and the field-type is not supposed to be a document-store like eg. in MongoDB. Compare the Postgres documentation