Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
JSONB型でPostgres
をNoSQLっぽく使う
@tacke_jp
Mixi, Inc.
Nohana, Inc.
2015.10.16 第10回若手Webエンジニア交流会 LT
$ whoami
• @tacke_jp
• 株式会社ミクシィ / 株式会社ノハナ
• サーバーサイドとかネイティブアプリとかデータ
解析とか色々やってる器用貧乏エンジニア
• 最近はPostgreSQLで変態クエリを書くことに

ハマっています
(AD)We re Hiring!!
• 株式会社ノハナでは「家族の問題を解決
する」というミッションに共感して一緒
に働いてくれる仲間を募集しています
• 現在は、日本中のお子さんを持つご家庭
で、スマホに撮りためた子供の写真を形
に残すこと...
Postgres Meets Schemaless
スキーマレスのメリット
• マイグレーションなしに
フィールドを追加できる
• 入れ子オブジェクトのよう
な木構造のデータが扱える
RDBのメリット
• 枯れてる
• トランザクションが使える...
JSONファイルのimport
Line-delimited JSON file
// posts.json
{“id”:1,"tags":["book","haskell"],"title":"すごいH本読ん
だ","created_at":"2015-08-16T14:10...
COPY文でimport
create table post_json (
obj jsonb
);
-- JSONB型のカラムのみを持つテーブルを用意
copy post_json
from '/path/to/json/file/posts...
Bulk Insert
create table comment_json (
obj jsonb
);
insert into comment_json
values('{"id":1,"post_id":1,"comment":"わかる"}...
JSONB型カラムへのクエリ
オブジェクトの中身を参照
select (obj->>'id')::integer as id
, obj->>'title' as title
, obj->>'created_at' as created_at
, obj->'tags'-...
オブジェクトの中身を参照
id | title | created_at | first_tag
----+----------------------------+--------------------------------+------...
JOIN
select post_json.obj->>'title'
, comment_json.obj->>'comment'
from post_json
, comment_json
where true
and post_json....
JOIN
?column? | ?column?
----------------------------+----------
すごいH本読んだ | わかる
Dockerがどっかーにいった件 | ウケる
SchemeかわいいよScheme |...
B-Tree Indexをそのまま張るのはNG
create index post_json_obj
on post_json using btree (obj);
create index comment_json_obj
on commen...
式Index
create index post_json_id
on post_json using btree ((obj->>'id'));
create index comment_json_post_id
on comment_jso...
Key名が決まらないならGIN※を
使おう
create index post_json_gin
on post_json using gin (obj jsonb_path_ops);
create index post_json_tags_...
その他
JSON配列に対する集約関数
select obj->>'id'
, ( select sum(length(value))
from jsonb_array_elements_text(obj->’tags')
)
from post_jso...
JSON配列に対する集約関数
?column? | sum
----------+-----
1 | 11
2 | 6
3 | 10
(3 rows)
SchemafulなテーブルへのINSERT
create table post (
id integer
, tags jsonb -- flatに出来ないものはjsonbに突っ込んじゃう
, title text
, created_at ...
SchemafulなテーブルへのINSERT
id | tags | title | created_at
----+---------------------+----------------------------+------------...
PostgresのJSONB型まとめ
• JSONは1つの値として扱われる (1カラム = 1つのJSON)
• B-tree IndexやGINが貼れる
• jsonb_array_elements(_text) や jsonb_popula...
最後におまけ
こんなJSONがありまして
{"car":"T","cdr": {"car":"h","cdr": {"car":"a","cdr":
{"car":"n","cdr": {"car":"k","cdr": {"car":" ","cdr":
...
こんなクエリを実行すると
with recursive eval as (
select obj->>'car' as car
, obj->'cdr' as cdr
, 1 as depth
from lisp
union all
selec...
ありがとうございました!
msg
---------------------------
Thank you for listening!!
(1 row)
Upcoming SlideShare
Loading in …5
×

JSONB型でpostgresをNoSQLっぽく使う

1,103 views

Published on

第10回若手Webエンジニア交流会 #wakateweb
でLTした際のスライドです。

Published in: Technology
  • Be the first to comment

JSONB型でpostgresをNoSQLっぽく使う

  1. 1. JSONB型でPostgres をNoSQLっぽく使う @tacke_jp Mixi, Inc. Nohana, Inc. 2015.10.16 第10回若手Webエンジニア交流会 LT
  2. 2. $ whoami • @tacke_jp • 株式会社ミクシィ / 株式会社ノハナ • サーバーサイドとかネイティブアプリとかデータ 解析とか色々やってる器用貧乏エンジニア • 最近はPostgreSQLで変態クエリを書くことに
 ハマっています
  3. 3. (AD)We re Hiring!! • 株式会社ノハナでは「家族の問題を解決 する」というミッションに共感して一緒 に働いてくれる仲間を募集しています • 現在は、日本中のお子さんを持つご家庭 で、スマホに撮りためた子供の写真を形 に残すことのお手伝いをしています • ご興味があればフリートーク時にお声が けください!! 家族を笑顔にしまくってくれるスマホアプリエンジニアさんに会いたい! https://www.wantedly.com/projects/25788
  4. 4. Postgres Meets Schemaless スキーマレスのメリット • マイグレーションなしに フィールドを追加できる • 入れ子オブジェクトのよう な木構造のデータが扱える RDBのメリット • 枯れてる • トランザクションが使える • JOINが使える • 豊富な関数群 Postgres※はJSONを値としてカラムにいれるようにすることで、 両者のいいとこ取りをした (※ JSON型はPostgres9.3以降、JSONB型は9.4以降でサポート)
  5. 5. JSONファイルのimport
  6. 6. Line-delimited JSON file // posts.json {“id”:1,"tags":["book","haskell"],"title":"すごいH本読ん だ","created_at":"2015-08-16T14:10:02Z"}⏎ {"id":2,"tags":["docker"],"title":"Dockerがどっかーにいっ た件","options":{"private":true},"created_at":"Wed Oct 14 13:50:09 +0000 2015"}⏎ {"id":3,"tags":["lisp","scheme"],"title":"Schemeかわいい よScheme","created_at":"2015-10-16T14:10:02+09"}⏎
  7. 7. COPY文でimport create table post_json ( obj jsonb ); -- JSONB型のカラムのみを持つテーブルを用意 copy post_json from '/path/to/json/file/posts.json' with csv quote e'x01' delimiter e'x02' ; (http://adpgtech.blogspot.jp/2014/09/importing-json-data.html)
  8. 8. Bulk Insert create table comment_json ( obj jsonb ); insert into comment_json values('{"id":1,"post_id":1,"comment":"わかる"}') ,('{"id":2,"post_id":2,"comment":"ウケる"}') ,('{"id":3,"post_id":3,"comment":"神"}') ;
  9. 9. JSONB型カラムへのクエリ
  10. 10. オブジェクトの中身を参照 select (obj->>'id')::integer as id , obj->>'title' as title , obj->>'created_at' as created_at , obj->'tags'->>0 as first_tag from post_json ; -- 演算子 -> , ->> でオブジェクトや配列の中身 にアクセス
  11. 11. オブジェクトの中身を参照 id | title | created_at | first_tag ----+----------------------------+--------------------------------+----------- 1 | すごいH本読んだ | 2015-08-16T14:10:02Z | book 2 | Dockerがどっかーにいった件 | Wed Oct 14 13:50:09 +0000 2015 | docker 3 | SchemeかわいいよScheme | 2015-10-16T14:10:02+09 | lisp (3 rows)
  12. 12. JOIN select post_json.obj->>'title' , comment_json.obj->>'comment' from post_json , comment_json where true and post_json.obj->>’id' = comment_json.obj->>'post_id' ;
  13. 13. JOIN ?column? | ?column? ----------------------------+---------- すごいH本読んだ | わかる Dockerがどっかーにいった件 | ウケる SchemeかわいいよScheme | 神 (3 rows)
  14. 14. B-Tree Indexをそのまま張るのはNG create index post_json_obj on post_json using btree (obj); create index comment_json_obj on comment_json using btree (obj); -- obj全体を = で比較する場合しかindexが使われない
  15. 15. 式Index create index post_json_id on post_json using btree ((obj->>'id')); create index comment_json_post_id on comment_json using btree ((obj->>'post_id'));
  16. 16. Key名が決まらないならGIN※を 使おう create index post_json_gin on post_json using gin (obj jsonb_path_ops); create index post_json_tags_gin on post_json using gin (obj->'tags'); -- 配列に対するindexも貼れる (※ GIN = Generalized Inverted Index)
  17. 17. その他
  18. 18. JSON配列に対する集約関数 select obj->>'id' , ( select sum(length(value)) from jsonb_array_elements_text(obj->’tags') ) from post_json ; -- PostgresではSELECT式でサブクエリが使える
  19. 19. JSON配列に対する集約関数 ?column? | sum ----------+----- 1 | 11 2 | 6 3 | 10 (3 rows)
  20. 20. SchemafulなテーブルへのINSERT create table post ( id integer , tags jsonb -- flatに出来ないものはjsonbに突っ込んじゃう , title text , created_at timestamptz ); insert into post select (jsonb_populate_record(null::post, obj)).* from post_json ; select * from post;
  21. 21. SchemafulなテーブルへのINSERT id | tags | title | created_at ----+---------------------+----------------------------+------------------------ 1 | ["book", "haskell"] | すごいH本読んだ | 2015-08-16 23:10:02+09 2 | ["infra", "docker"] | Dockerがどっかーにいった件 | 2015-10-14 22:50:09+09 3 | ["lisp1", "scheme"] | SchemeかわいいよScheme | 2015-10-16 14:10:02+09 (3 rows)
  22. 22. PostgresのJSONB型まとめ • JSONは1つの値として扱われる (1カラム = 1つのJSON) • B-tree IndexやGINが貼れる • jsonb_array_elements(_text) や jsonb_populate_record(set) は
 リレーショナルな世界とJSONな世界との橋渡し役 • (ちなみに)9.5ではオブジェクトや配列に要素を追加したり削除 したりできるようになる (参考: http://www.slideshare.net/hadoopxnttdata/postgresql-95-new-features-nttdata )
  23. 23. 最後におまけ
  24. 24. こんなJSONがありまして {"car":"T","cdr": {"car":"h","cdr": {"car":"a","cdr": {"car":"n","cdr": {"car":"k","cdr": {"car":" ","cdr": {"car":"y","cdr": {"car":"o","cdr": {"car":"u","cdr": {"car":" ","cdr": {"car":"f","cdr": {"car":"o","cdr": {"car":"r","cdr": {"car":" ","cdr": {"car":"l","cdr": {"car":"i","cdr": {"car":"s","cdr": {"car":"t","cdr": {"car":"e","cdr": {"car":"n","cdr": {"car":"i","cdr": {"car":"n","cdr": {"car":"g","cdr": {"car":"!","cdr": {"car":"!","cdr":{} } }}} }}} }}} }}} }}} }}} }}} }}}
  25. 25. こんなクエリを実行すると with recursive eval as ( select obj->>'car' as car , obj->'cdr' as cdr , 1 as depth from lisp union all select cdr->>'car' as car , cdr->'cdr' as cdr , depth + 1 as depth from eval where (cdr->'cdr') is not null ) select string_agg(car, '' order by depth) as msg from eval ; (https://gist.github.com/yuki-takeichi/c4812f7af46f7f84e958)
  26. 26. ありがとうございました! msg --------------------------- Thank you for listening!! (1 row)

×