夏は少し憂鬱

夏は少し憂鬱

夏が憂鬱なのは、ある種の儚さがそこにあるからだと、ずっとそう思っていた。

違和感に気付いたのは社会人になって数年経ってからだった。どうも自分のパフォーマンスが悪い。遅刻気味になるし、仕事も日中は手につかず夕方ぐらいにようやく頭が回り出してくる。思考しようにも数秒に一回突然チャンネルを切り替えられるかの如く、思考が中断する。咄嗟に物事が思い出せない。出来るはずの自分がいなくなり、人とのコミュニケーションがうまくいかず、ネガティブなサイクルに陥りがちになる。

そういえば学生の時も、夏は人に会いたくなかったり、体調不良を理由にして家にこもり気味だったりした。小学6年生の時に夏休み見たい映画を見て面白かったのに何故か虚無感を抱いた違和感から始まり、高校では一年の夏休みに部活に行けなくなり、大学では当日に遊びの予定をドタキャンしたりした。自分はそういう人間なのだと思っていた。

カウンセリングで相談したところ、こういうことで悩んでいる人は一定数いるのだ、ということを教えてもらった。気温差がある環境だと自律神経が乱れやすい、と。こんなにも季節の気温の変化に、自分の思考力が影響を受けるとは思っていなかった。また調子の浮き沈みはあるべきものだ、とも諭された。常に調子のいい状態が続くとカウンセラーとしては逆に怖い、と。

正直、調子が悪いと未だに良い時の自分の幻想がチラつく。しかし、悪い時はずっと続くものではないとも思えるようになった。要因が掴め、ある程度調子が予測できるようになったので、うまく仕事や日常タスクのバランスを取って、自分で自分の首を絞めないようにしようと思う。

調子のいい今、このことをここ纏めておくことで、後々振り返れるようにしておくことにする。また、もしこのことが悩んでいる誰かのヒントになれば望外の喜びである。

DroidKaigi2019にUnicode Emojiをテーマに登壇した話 (あるいはAndroidのテキストスタックについて)

2019/2/7 ~ 2/8で開催されたDroidKaigi2019に参加・登壇してきたのでそのお話を書きます。

経緯

所属している株式会社ノハナのフォトブックアプリで、入稿画像を生成するためのバックグラウンドジョブを実行するサーバーがあって、しばらくはそのテキスト周りをいじっていてオープンソースのテキストスタック周りに詳しくなってたんですね。(PangoとかCairoとかFreeTypeとかHarfBuzzとか、そのへんのライブラリです。)

で、なんとなくコミットログ漁ってたらPango / Cairoがどうやらカラー絵文字(Unicode Emoji)の描画に対応してることがわかったんで、いろいろ試行錯誤したのち、最終的にプロダクトに入れました。(その時の試行錯誤の一部は http://foobar.hatenablog.com/entry/2017/12/24/183707 この辺にまとまっています。)

で、サーバー側はTwemojiフォントで描画できるようになったんですが、アプリはOS側の絵文字フォントが表示されるので、印刷物とUIとで見た目が変わるじゃんどうすんのよ、って話になったわけです。Androidのテキスト周りは基本的にオープンソースのライブラリに依存しているので(FreeTypeとかHarfBuzzとか)、絵文字フォントファイルの形式(CBDT/CBLC)とか描画ロジックも基本的なところはサーバーと一緒なんですよ。なので、これ頑張ればAndroidでも独自の絵文字フォントでUnicode Emoji描画いけるんじゃね?ってことで試行錯誤したら、いけちゃったんですよね。(ちなみにiOSはemojicaってライブラリを使ってます。)

でいろいろ知見が溜まったところに @seto_hi にDroidKaig2019のCfPにUnicode Emojiで出さないかと勧められて、せっかく知見が溜まったし自分自身の整理も兼ねて吐き出す場を作るかと思い、応募してみました。正直かなりマニアックな内容だったので、まぁ通ればラッキーかな...ぐらいに思っていたのですが、ありがたいことに採択頂いたのでこのような発表の場が実現したわけです。

発表

こちらが当日のスライドです。

speakerdeck.com

動画はこちら。

youtu.be

本当はもっといろいろ実装周りとか紹介したかったんですが30分短すぎて無理ゲーと思いながら発表原稿作ってました。発表の前々日にスライドの完成を祝うかのようにUnicode Emoji 12.0がリリースされました。スライドは修正を余儀なくされました。

直前にRoom 4からRoom 1へと大きな部屋に変更となり、さらにセッション枠は @mhidaka さんの裏番組だったので、どうかなーと思ってたんですが、部屋半分ぐらい埋まってたので良かったです。

発表直前にEmojiCompatを使ってる人に手を挙げてもらったら、数人だったので意外でした。

出会い

GoogleAndroidなどのtext周りをやっているnonaさん(@ttuusskk)やTATEditorの作者の六々さん(@496_)にお会いしてお話することができました。

テキスト周りやっている人周りにあまりいないため、マニアックな話で盛り上がれて楽しかったなと。実装者としてコードに触れているからこそ出てくる話題は生々しくていいですね。リアリティのある話は面白いし説得力がありました。

お2人の発表、必見です。

nonaさんはAndroidのtext周りのパフォーマンスチューニングなので、Androidのtext周りの実装にdeep dive出来ます。Android OS開発者だからこそ出来る話に説得力がありました。

[DroidKaigi 2019] The best practice for Android Text - Speaker Deck

DroidKaigi 2019 - Best practice for text on Android and its internals. / Seigo Nonaka [JA] - YouTube

六々さんの話はテキストエディタを自作する=テキストスタック全てが関わる話なので、テキスト周りの全体像を把握するのに丁度いいかなと。一般論から実装まで丁寧に説明されていた印象です。

ゼロから実装する縦書きTextViewとその周辺技術 / Vertical TextView from Scratch and Related Technologies - Speaker Deck

DroidKaigi 2019 - ゼロから実装する縦書き TextView とその周辺技術 / 六々 (@496_) [JA] - YouTube

お二人の発表を聞いたりオフィスアワーでお話して思ったこととしては、Internationalizationは辛いけど、ここ数年でものすごい勢いで整備されて来てるなと。長らく実装が仕様に先行している領域でしたが、文字文化の進化スピードにUnicodeや各OSの実装は十分追いついていると思いました。増殖し続ける絵文字やスタンプを除いて...

最後に

自分の発表を聞きに来て頂いた皆様、ありがとうございました。気になる点があればこのブログのコメントやtwitterなどで是非。ノハナ社のAndroidエンジニアの皆様、スライドや発表のレビューありがとうございました。また、DroidKaigi2019スタッフの皆様お疲れ様でした。素敵な発表の場をいただきありがとうございました。

Promiseに出来てasync/awaitに出来ないこと (JavaScriptを例として)

JavaScriptで非同期処理をasync/awaitを使って同期的なスタイルで書いていると、すべてのコードをそのスタイルで統一して書きたくなる。なので非同期処理を開始して実行を明け渡したいときはもちろんawaitを使うし、非同期処理に失敗したときはtry-catch構文で例外ハンドラに制御が移るようにする。ただ、同期的なスタイルで書けない処理が存在するために、どうしてもすべてを統一することはできない。Direct styleで書けないcontrolは継続渡しスタイル(CPS)を使って書くしかないからだ。

JSの場合でいうと、並行制御周りがそれにあたる。Promise.all()Promise.race() などは対応する構文がJS側に存在しない。 例えば Promise.all() に対応する awaitall みたいな構文が言語側に欲しくなる。こんなふうに:

const [x, y] = awaitall f(), g();

(完全なコード)

const [x, y] = await Promise.all([f(), g()]);

と書けば見栄えとしてはほぼ変わらないが、Promiseは結局のところコールバック関数(明示的継続)のコンビネータにすぎないので、Promiseを使うとCPSで書いていることになる、と自分は思う。

そもそもJSには並行制御の構文は存在しないので、すべてをdirect styleで置き換えようとするのには無理がある。それはこれまでランタイムがコールバックスタイルで並行制御APIを提供してきたためにCPSで書くことを強いられてきたという歴史があるからだ。逆に考えると、並行制御のための構文が存在しなくても並行制御が書けるというの驚くべきことだ。それだけCPSは表現力が高い。

参考

Noto Emojiにコントリビュートし縦書きできるようにした話

こんにちは、株式会社ミクシィのグループ会社の株式会社ノハナでエンジニアをしている @tacke_jp です。この記事はmixiグループ Advent Calendar 2017の24日目の投稿です。今日は少しばかりマニアックな話題になりますが、縦書きの絵文字の話をしたいと思います。

絵文字を含むテキストの縦組みレンダリングオープンソースで実現する手段はこれまで充実していませんでした。今回、オープンソースで絵文字の縦組みを実現するための技術調査を行いました。またその過程でNoto Emojiで縦組みが正常に行えなかったため、それを解消しnoto-emojicontributeを行いました

どのフォーマットを選択すべきか

絵文字フォントの規格には様々なものがあります。代表的なものはOpenTypeフォント内に独自のテーブルを定義し、そこにカラーのビットマップやSVGを入れる方式となっています。各社推しているフォーマットがばらばらで統一されておらず、足並みが揃っていないのが現状です。

オープンソースのライブラリFreeTypeは様々なフォントのフォーマットに対応しており、グリフのアウトライン情報(絵文字の場合は画像データ)やメトリクスを取得するための抽象レイヤを提供してくれます。そのためオープンソースで絵文字のテキストレンダリングをする場合はFreeTypeが対応しているフォーマットを選択することになるかと思います。今回の検証ではFreeTypeが対応しており、公開されているフリーの絵文字フォントの多くで採用されているCBDT/CBLCを利用しました。

オープンソースでの日本語テキストレイアウトの技術スタック

オープンソースで公開されており、GTK+などで採用されているテキストレイアウトエンジンにPangoがあります。

PangoはグラフィックバックエンドとしてCairoを利用することができます。CairoはGTK+などで用いられており、過去ChromeFirefoxなどでも採用されていたグラフィックエンジンです。Cairoが最近のアップデートv1.15.8でカラー絵文字に対応したため、Cairo+FreeTypeでのカラーグリフを含めたテキストレンダリングが実現可能となりました。

オープンソースのテキストレイアウトの技術スタックについては、State of Text Renderingに詳しくまとまっています。

NotoColorEmoji.ttfで絵文字の縦組みがズレる原因とその対応

Pango+Cairoで縦組みを試したところ、以下のように絵文字のみ描画位置がずれてしまっていました。

f:id:tacke_jp:20171224182900p:plain

Pangoでは_pango_fc_shapeで、cairoにグリフをレンダリングレンダリングする位置を指示しています。デバッガを噛ませて調べてみると、絵文字グリフのみFreeTypeから取得したメトリクスが縦組みの場合に限りすべて0になっており、そのためにグリフが全て同じ位置に重なるようにして描画されていることがわかりました。

さらにFreeTypeの中を追っていくと、OpenTypeフォントファイルのdecoderに行き着き、絵文字グリフの場合のメトリクスの取得にはNotoColorEmoji.ttfの場合はsmallGlyphMetricsという形式でメトリクスが含まれており、この場合に縦組みのメトリクスはすべて0初期化されるということが分かりました。

bigGlyphMetricsを使えば縦組み用のメトリクスをPangoまで渡すことができるため、noto-emojiのフォントファイル作成スクリプトに手を入れ、smallGlyphMetricsの代わりに、適切な縦組み用のメトリクスを含めたbigGlyphMetricsを使用するようにしました

f:id:tacke_jp:20171224182820p:plain

ご覧のように正しい位置にレイアウトされるようになりました。(画像生成には https://gist.github.com/yuki-takeichi/f9c1b7bf9154597518fbd9289b527004 を用いました。)

素敵な絵文字縦組みライフを

このcontributeにより、FreeTypeで特別な対応を行わなくても絵文字の縦組み用のメトリクスが取得できるようになり、絵文字縦組みの難易度が下がりました。例えばGTK+環境でNoto Emojiを用いた縦組みテキストのビューアやエディタが作りやすくなったのではないかと思います。また、NotoColorEmoji.ttfの生成に必要なファイルは全部noto-emojiにあるので、PNG画像があればnoto-emojiのemoji_builder.pyを利用して縦組みに対応したカスタム絵文字フォントを作成することができます。時間が許せば別のCBDT/CBLCを使用した絵文字フォントにもbigGlyphMetrics化のcontributeができればと思っています。

明日、ラストを飾るのは @isaoshimizu さんの「2017年の活動報告 - SREとビールの話」です。

それでは皆さんよい年末を〜

(追記 2019.2.9)

DroidKaigi2019で @ttuusskk さんに教えていただいたところによると、Androidでは絵文字のメトリックはHarfBuzzまで聞きに行っているらしく、そのHarfBuzzがsmallGlyphMetricsのテーブル(format17)しか読めないため、結局noto-emojiではsmallGlyphMetricsがデフォルトになるように再度スクリプトが変更されたとのことでした。 Some tooling updates for android u11. · googlei18n/noto-emoji@e571d53 · GitHub

ということなので、AndroidのHarfBuzzがupstreamになったら再チャレンジしようかな...

私たちが奴隷ではなく自由人になるためには一週間でどのくらい自由な時間が必要か

ニーチェは著作「人間的な、あまりに人間的な」の中で次のような格言を残している。

『あらゆる人間は、いかなる時代におけるのと同じく、現在でも奴隷と自由人に分かれる。自分の一日の三分の二を自己のために持っていない者は奴隷である。』

現代日本の被雇用者(サラリーマン)は奴隷なのだろうか、それとも自由人なのだろうか。実際に計算して検証してみた。

法定労働時間は1日8時間だ。法定労働時間の定義は「労働者が使用者に労務を提供し使用者の指揮命令に服している時間」で、つまり言葉を選ばずに言うと奴隷として働いている時間ということになる。 (もちろん自分のやりたいことをして楽しく労働している人もたくさんいるだろう。しかし、ここではあくまで法律上の義務が生じるという点から労働時間は自分の自由にならない時間とみなす。)

一般的な例では、1日の労働時間は8時間だが休憩も合わせて拘束時間が9時間となることが多い。日本人の平均通勤時間は往復で約1.3時間*1なのでこの時間も自己の時間と見なせない。そうなると10.3時間が自分の自由にならない時間だ。

厚生労働省によると、健康を維持するのに推奨される睡眠時間はだいたい7時間だ*2。そうすると日中活動できる時間は17時間となる。一週間では119時間だ。労働が平日の5日間行われるとすると、10.3時間が5回の51.5時間が奪われる。すると自分の自由になる時間は67.5時間だ。割合にすると約57パーセントが自分の自由の時間の割合だ。自分の1日の3分の2を自己のために持つには67パーセント以上の割合が必要なので、この場合のサラリーマンはニーチェの定義では奴隷ということになる。

(フリーランスで週4日働くとすると65パーセントだ(惜しい!) 週3日のフリーランスで生活できている人は十分に自由人だと言える。)

ところで、この100年間で先進国での平均労働時間は減少の一途を辿っている*3。あれほど長時間労働が騒がれている日本でもこの50年間で年間の平均労働時間は600時間も減少している(一週間では11時間程度)*4。科学技術の発展による生産性の向上が労働時間の減少に寄与しているのは明らかなので、この先も技術の進歩が止まらないうちは労働時間は減り続ける。そのうち自己の自由になる時間が3分の2(一週間で79時間)を超え、サラリーマンも全員奴隷状態を脱し自由人になれる日が来るかもしれない。

自由を手にした時、私たちは改めて人生における目的の設定、つまり「どう生きるか?」という別の問題を解決しないといけなくなる。

『自分自身に命令しない者は、いつになっても下僕にとどまる。』(ゲーテ)

奴隷状態からは脱出できても下僕のままで一生を終えないように、私たちはまだまだ賢くならないといけないようだ。

そんじゃーね。

プログラミングができると何ができるようになるのか (またはプログラミングに飽きてきたら読み返す文章)

この前、とあるプログラマではない人に「プログラムが書けると何ができるようになる?」と聞かれた。

その時は、やろうと思えばなんでもできる答えたが、その後ちょっと考えて、但し無限の時間と無限の計算機資源があればと付け加えた。

チューリング完全な言語を使いノイマン型コンピュータに命令させれば、停止するコードが書ければどんな計算だってさせることができる。しかし、現実的には有限のコンピュータリソースに、有限のストレージに、有限のネットワーク帯域しかない。その制約の中で、現在あるところの技術を組み合わせることで、 実際に に何かを「できる」ようにする、というのはエンジニアリングの本質だと思うし、その中でいかに性能が出て保守性が高いコードを書くかというのが、ソフトウエアエンジニアの腕の見せ所だと思う。

(ソフトウエアに限った話ではないとは思うが)ソフトウェアエンジニアの仕事は泥臭く、制約が多い窮屈な中でコードを書かなければならないと感じる。それは僕らが実現しようとしていることが、今この世界の複雑さに影響を受けながら、現実の今ここにある問題を解こうとしていることの証左だと思う。

なのでそれは受け入れるべき制約だとは思うが、その窮屈さに毎日触れているといやになってくるかもしれない。そんな時は、この現実に存在するある制約が外れたときにどんなことが「できる」ようになるのか考えると楽しい。

今の100倍core数が増える、ネットワークが100倍早くなる、メモリが100倍に増える。もしくはCPUが現在の1/100のコストになる。そんなようなことが起こった未来では僕らはどんな言語でプログラミングしているのか。どんなシステムアーキテクチャを採用してサービスを運営しているのか。などを想像してみると、次にどんなコードを書くべきかアイディアが湧いたり、モチベーションが出てきたりするかもしれない。

少なくとも自分はそんな気がする。

PostgreSQL9.5の強力なJSON関数で、ネストした構造を持つJSONを自在に組み立てる

(この記事はmixi advent calendar5日目の投稿です。)

年内にリリース予定のPostgreSQL9.5ではjsonb型のデータを操作する関数が拡充され、より自由自在にJSONを扱うことが可能となります。この記事ではPostgreSQL9.5を用い、REST APIJSON生成部分を SQLのみで 実現するサンプルコードを紹介することで、「なにこれ、こんなことできるならORM使う必要なくなるじゃん」みたいな感じで皆さんに驚いてもらうことを目的としています。[1]

以下、次に示すスキーマを持つブログサービスのREST APIを題材として話を進めます。

create table "User" (
  id varchar(10),
  name varchar(20)
);

create table "Post" (
  id int,
  user_id varchar(10),
  content text
);

create table "Reply" (
  id int,
  user_id varchar(10),
  post_id int,
  message text
);

insert into "User" (id, name) values('hoge', 'ほげ'),('foo', 'ふう'),('bar', 'ばあ');
insert into "Post" (user_id, content) values(1, 'hoge', '今日は天下寿司を食べた');
insert into "Post" (user_id, content) values(2, 'foo', 'ぬるぽ');
insert into "Reply" (user_id, post_id, message) values('foo', 1, 'いいね!'),('bar', 1, '炙りサーモンある');
insert into "Reply" (user_id, post_id, message) values('bar', 2, 'ガッ');

まずはじめに、ユーザーのIDを指定してオブジェクトを取得するエンドポイント/users/:idを作ります。

select to_jsonb("User") as "/users/:id"
from "User"
where id = $1
;

このクエリは、特定のidを持つユーザーのJSON Objectを生成するクエリです。to_jsonbは様々な型の上に定義されていますが、row型を引数として渡すと(このクエリではテーブルの参照であるUserという識別子を渡しているためrow型を渡していることになります)、カラム名をキーとしたJSON Object(jsonb型)を返します。プリペアドステートメント引数$1にhogeを代入してクエリを実行した結果が以下になります。

 {"id": "hoge", "name": "ほげ"}

期待する結果になってますね!

次に、すべてのユーザーのリストを取得するエンドポイント/usersを作ります。Userオブジェクトの配列を取得するクエリは次のようになります。

select jsonb_set('{}'::jsonb, '{users}', jsonb_agg("user")) as "/users"
from (
  select to_jsonb("User") as "user"
  from "User"
) t
;

先ほど紹介したto_jsonbでUserレコードをjsonb型に変換した後、それを集約関数jsonb_aggに渡してArrayにしています。さらにそれをusersをキーとするObjectで包んでいます。

結果は下。ちゃんとデータがネストされてる!

{"users": [{"id": "hoge", "name": "ほげ"}, {"id": "foo", "name": "ふう"}, {"id": "bar", "name": "ばあ"}]}

では次に、すべての記事のリストを取得する/postsを作ります。 その際、オブジェクトにはuser_idではなく、Userオブジェクトそのものをネストして含めたいとします。クエリはこんな感じになります。

select jsonb_set('{}'::jsonb, '{posts}', jsonb_agg("post")) as "/posts"
from (
  select jsonb_set(to_jsonb("Post") - 'user_id', '{user}', to_jsonb("User")) as "post"
  from "Post"
     , "User"
  where "Post"."user_id" = "User"."id"
) t
;

to_jsonb("Post") - 'user_id'はPostをObject化した後user_idキーを取り除いています。それをjsonb_set関数に渡してuserをキーとしてUserのオブジェクトを値として追加しています。

結果は下。超便利。

 {"posts": [{"id": 1, "user": {"id": "hoge", "name": "ほげ"}, "content": "今日は天下寿司を食べた"}, {"id": 2, "user": {"id": "foo", "name": "ふう"}, "content": "ぬるぽ"}]}

さらにPostオブジェクトに、Replyオブジェクトの配列を持つrepliesフィールドを追加したいとします。クエリはこうなります。

select jsonb_set('{}'::jsonb,
                 '{posts}',
                 jsonb_agg(jsonb_set("post" - 'user_id',
                                     '{user}',
                                     to_jsonb("User")))) as "/posts"
from (
  select jsonb_set(to_jsonb("Post"), '{replies}', jsonb_agg("Reply")) as "post"
       , "Post"
  from "Post"
     , "Reply"
  where "Reply"."post_id" = "Post"."id"
  group by "Post"
) t
   , "User"
where ("Post")."user_id" = "User"."id"
;

group by "Post"でPostテーブルのレコードを1つの値として扱っているのがミソです。こういうふうに構造をネストさせるクエリが自在に書けるのがPostgresの素晴らしいところ。

結果は以下。

{
  "posts": [
    {
      "id": 1,
      "user": {
        "id": "hoge",
        "name": "ほげ"
      },
      "content": "今日は天下寿司を食べた",
      "replies": [
        {
          "id": 2,
          "message": "炙りサーモンある",
          "post_id": 1,
          "user_id": "bar"
        },
        {
          "id": 1,
          "message": "いいね!",
          "post_id": 1,
          "user_id": "foo"
        }
      ]
    },
    {
      "id": 2,
      "user": {
        "id": "foo",
        "name": "ふう"
      },
      "content": "ぬるぽ",
      "replies": [
        {
          "id": 3,
          "message": "ガッ",
          "post_id": 2,
          "user_id": "bar"
        }
      ]
    }
  ]
}

(結果は見やすいようにprettifyしています。)

ついでに、集約関数を使ってみたいので、postオブジェクトに返信の数を表すreply_countというフィールドを追加してみます。 7行目を以下のように変更します。

-  select jsonb_set(to_jsonb("Post"), '{replies}', jsonb_agg("Reply")) as "post"
+  select jsonb_set(jsonb_set(to_jsonb("Post"), '{replies}', jsonb_agg("Reply")), '{reply_count}', to_jsonb(count("Reply"."id"))) as "post"

実行結果は以下です。

{
  "posts": [
    {
      "id": 1,
      "user": {
        "id": "hoge",
        "name": "ほげ"
      },
      "content": "今日は天下寿司を食べた",
      "replies": [
        {
          "id": 2,
          "message": "炙りサーモンある",
          "post_id": 1,
          "user_id": "bar"
        },
        {
          "id": 1,
          "message": "いいね!",
          "post_id": 1,
          "user_id": "foo"
        }
      ],
      "reply_count": 2
    },
    {
      "id": 2,
      "user": {
        "id": "foo",
        "name": "ふう"
      },
      "content": "ぬるぽ",
      "replies": [
        {
          "id": 3,
          "message": "ガッ",
          "post_id": 2,
          "user_id": "bar"
        }
      ],
      "reply_count": 1
    }
  ]
}

SQL15行でここまで出来る、すごい!

まとめ

  • レコードをJSONに変換する場合はto_jsonbを使う
    • 行を1つの値(ROW型)として扱えるところにPostgresの妙がある
  • Objectに値を追加する場合はjsonb_set、削除する場合は-を使う
  • 複数のJSONB型を配列にするには集約関数jsonb_aggを使う

[1] ここでのORMとは、クエリを部品化して合成可能にする機能ではなく実行結果をModelと呼ばれるオブジェクトに移し替える機能のことを指しています。(例えばActiveRecordなど。)







--

以下はポエムなので適当に読み流して下さい。

RDBSのクエリって、保存された正規化されたテーブルをJOINして(=つまり非正規化して)データを見たい形/使いたい形に加工するためのものなので(参考:データベースの三層モデル) このスライドでも言及されてるけど、第一非正規なデータを返すことだって出来ていいはず。なぜ我々は正規化されたテーブルからjsonを作るのにRailsをかます必要があるのか?というのは以前から疑問に思っていたので、Postgres9.5のJSON関数に出会った時は強く感動した。

REST APIを書くときにサーバーサイドMVCは必要ないよね、っていうのは以前から思っていて、DBから取得したデータをわざわざModelオブジェクトにmappingして、クライアントに返すときにはシリアライズしているのは無駄に思える。RDBSから結果がHashのArrayで帰ってくるなら、それを引数として受け取って、データを操作する関数を書けばそのほうがシンプルにすむのに。そしてそれをそのままJSONに変換するだけでいいじゃん。

そういうわけで、JSON APIを書こうとした時に、データの加工という本来のDBの役割をPostgresはしっかり果たしてくれるわけです。ネストしたデータ構造だって自在に組み立てれるんです。不要なレイヤを挟まずにアーキテクチャをシンプルに保てるというのは素晴らしいことだと思うのです。

あと、Postgresのクエリの表現力はすごいというのを伝えたい。MySQL5.7にもJSON対応が入ったけど、上で紹介したようなことは出来ない。Postgresにはrow型が存在しているおかげでネストしたJSONを取得するクエリがシンプルに書ける。この記事では紹介出来てないけど、他にも多様なJSON操作関数があって、それが実現できているのはひとえにクエリの表現力のおかげ。LTした時のスライドで紹介したけど、ネストしたJSONをそのままjsonb型に突っ込んで、JSON内部のArrayに対して集約関数を実行するみたいなこともPostgreだからできる。

パフォーマンスの部分でMySQLにかなわない部分はあるとは思うけど、RDBの正当な進化の結果として現代的なJSONの機能を手にしており、ソースも常に読みやすくメンテナンスされており、Postgresはすごい。

以上、駄文失礼しました。