メモ。 調べた時のバージョンは、PostgreSQLは9.4.0で、Railsは4.2.0。
バイナリ型JSONの事。JSONBの"B"はバイナリ(binary)のB。
元々PostgreSQLでは9.2からJSON型に対応しており、9.4からJSONを独自バイナリ形式で格納するJSONB型が追加された。
JSONBの特徴をざっと書くと以下のような感じ
基本的に、検索に特化している感じらしい。 JSONBについての詳細は、@nuko_yokohamaさんのJSONBデータ型を使ってみようが大変詳しいのでおすすめです。
4.2からRails本体でJSONBに対応しているので、特にgem等は必要無し。型にjsonb
を指定してあげればOK。
bin/rails generate model event payload:jsonb
def change create_table :events do |t| t.jsonb :payload t.timestamps null: false end add_index(:events, :payload, using: 'gin') end
こんな感じで。インデックスはGINインデックスで。 GINインデックスについては前書いたので割愛。
Hashにしてから入れるのも、JSON形式のStringそのまま入れるのも、どっちも大丈夫そう。
Event.create(payload: { kind: "user_renamed", change: ["jack", "john"]}) Event.first.payload # => {"kind"=>"user_renamed", "change"=>["jack", "john"]} Event.create(payload: '{"title": "Example Schema", "type": "object", "properties": { "firstName": { "type": "string"}, "lastName": { "type": "string" } } }') Event.last.payload # => {"type"=>"object", "title"=>"Example Schema", "properties"=>{"lastName"=>{"type"=>"string"}, "firstName"=>{"type"=>"string"}}}
先に書いた通り、重複したキーは削除される。
Event.create(payload: '{"user_name": "test1", "user_name": "test2"}') Event.last.payload # => {"user_name"=>"test2"}
上記の場合、user_name
キーが重複しているので、最初に定義したtest1
の値が破棄される。
PostgreSQLではJSON用の演算子や関数が幾つか定義されているのですが、Rails本体では特にラッパーとかは提供されてないので、それらの演算子を使いたい場合は、普通にwhere
メソッドで指定してあげればOK。
Event.where("payload->>'name' = ?", "test1") # => #<:relation id: payload:>"test1"}, created_at: "2015-02-11 01:09:40", updated_at: "2015-02-11 01:09:40">]> </:relation>
演算子・関数の詳細はこちらを参照。
で、一個問題があって、JSONB用の一部の演算子に"?“ が使われていて、これがActiveRecordのプレースホルダ用の演算子(”?“)とバッティングしています。
なので、今はプレースホルダとJSONBの一部演算子を一緒にwhere
を使う事が出来ない。 使えないのはこの辺り。key/element内に指定して値がいるかどうかの存在チェック用演算子。 ActiveRecordのプレースホルダに”?“以外の記号を使えるようにするPRが見た覚えがあるので、いずれ改善されるかも。
ちょっと癖と一部演算子の問題があるものの、頻繁に検索するデータであれば、JSONB使うのが良さそうかなと。