データ分析 SQL とその実行結果を共有・検索できるアプリ Bdash Server を作りました

こんにちは。クックパッドでエンジニアをしている @morishin です。Bdash Server というデータ分析 SQL を共有するアプリケーションを作って社内で使い始めたのでその紹介をします。

クックパッドのサービス開発は「仮説を立てる」→「作ってリリース」→「効果検証」の繰り返しで進んでいます。ここで言う効果検証というのは作ったサービスが狙い通りの使われ方をし、ユーザーに価値を提供できているかどうかの確認のことです。その手段は複数あり、実際に使っていただいたユーザーさんにインタビューをさせていただく場合もあればアプリケーションから送信されたアクセスログ等を分析することで評価する場合もあります。この記事では後者の定量分析を効率化するためのツールを作った話をします。

アプリの概要

まずはアプリの機能を紹介します。Bdash Server は、Bdash という同僚 (@hokaccha) が作った SQL クライアントアプリと連携し、クエリと実行結果をウェブページとして共有することができるアプリケーションです。

Bdash クライアントアプリのついての詳細はこちらをご覧ください。

hokaccha.hatenablog.com

Bdash クライアントアプリを社内のデータウェアハウス (社内の巨大な分析用データベースで、クックパッドでは Amazon Redshift を使っている) に接続し SQL を書いて実行すると、このように実行結果がクライアントに表示されその結果のテーブルをグラフにすることもできます。

f:id:morishin127:20210610215433p:plain
Bdash クライアントアプリの画面

そして Bdash クライアントの共有機能から Bdash Server を選択すると次のようなウェブページが生成されブラウザで開かれます。共有されたクエリのページにはハッシュ値を含む個別の URL が振られ、簡単に社内共有することができます。

f:id:morishin127:20210609002638p:plain
Bdash Server の画面

Bdash Server にはクエリ共有機能に加えて次の目玉機能があります。

  • クエリに説明文が書ける🙌
  • ユーザー個別のクエリ一覧ページができる🙌
  • 他の人が書いたクエリが検索できる🙌

上のスクリーンショットでも SQL セクションの上に説明文が書かれていますね。ここに issue ページへのリンクを貼ったり分析に関する説明を書いたりすることができます。

ユーザーページではそのユーザーがこれまで共有したクエリが一覧できます。社内のデータ分析が得意なメンバーのページは分析 SQL 知見の宝庫であり様々なテクニックに出会うことができます。

ページ上部には検索窓があり、ここから共有されたクエリを検索することができます。検索スコープはクエリタイトル、説明文、SQL文となっており、「レシピ投稿数」や「DAU」といった日本語キーワードから「pv_logs」といったテーブル名、「partition by」といった SQL 関数のキーワードなどで過去の分析事例を見つけることができます。

リリース時の機能としては以上で、リリース後に有志によりクエリをお気に入りに登録する機能、クエリを手元で実行するためにクリップボードにコピーする機能などが追加されました。

ちなみに Bdash Server も Bdash も OSS です。Bdash Server はインフラを用意してデプロイする必要がありますが、Bdash クライアントアプリは単体でダウンロードして即お使いいただけるので是非ご活用ください。

github.com github.com

開発の動機

さてこのアプリを作った動機ですが、業務で日常的に書いている分析 SQL をもっと楽に書きたかったからです。SQL を書きながら「こんな数字絶対過去に誰かが出したことあるやん...」などと思うことが多く、できるならば過去に誰かが書いたやつをコピペしてちょっといじって完成させたいなと思っていました。また例えば「コンテンツ投稿数の移動平均」「機能利用のリテンションレート」「ある週の7日間のうちアプリを利用した日数の分布」などクエリに起こそうとするとウッと手が止まるような難しめの数字を出そうとした時に、誰かが出したやつがあれば参考にしたい〜〜と思っていました。

クックパッド社内で日常的に分析 SQL を書いているのは実はエンジニア職でない人も多数います。データ分析を専門で行う人が社内にいるわけではなく、サービス開発に携わるエンジニアやディレクター・デザイナーまでもが日々 SQL を書いて数値を睨んでサービスの方向性を考えています。SQL にまあまあ馴染みのあるエンジニアでさえ上述のようなことを思うのに、いわんやディレクター・デザイナーをやという感じです。余談ですが時々ディレクターの方がウィンドウ関数なんかも駆使した100行を超える SQL をバーンと書いているのを見かけてビビることがあります。

過去に人々が書いた分析 SQL が蓄積される場がありそこでキーワード検索ができれば毎回ゼロから分析 SQL を考えなくとも過去の事例を参考に効率的に分析をすることができるのではと考えました。実は Bdash クライアントには元々クエリと実行結果を GitHub (または GitHub Enterprise) の Gist で共有する機能がありとても便利に使っていましたが、クエリ単体を共有することはできるものの蓄積と検索は実現できていませんでした。

この想いを温め続けること約2年、今年の3月に社内で「Hackarade」というハッカソンイベントが開催され、この時間を使えば作れるのではと思い着手したのがきっかけとなりました。

開発の過程

ハッカソンイベント期間中に基礎的な実装をし、その後は業務の合間を縫って機能の拡充、社内デプロイ、OSS 化といった作業を進めていきました。

作りたい機能はたくさんあって構想は広がる一方ですが、ハッカソンということもあり時間は限られています。作り始める前に構想を複数のリリースフェーズに分け、最初に作り切る最小限の機能を定義しました。次の画像が実装を始める前に書いたメモで、ハッカソン中にフェーズ1まで作ることを目標にしていました。

f:id:morishin127:20210609003058p:plain
開発前のメモ

このような個人開発に限らず普段の業務でも仕様というものは膨らみがち。速くユーザーに価値を届けるためにも膨らんだ仕様を削ぎ落としたりリリースフェーズを分けてフィードバックを早く得るということは重要ですよね。

お客様の声

社内で利用され始め、毎日20件程度のクエリが共有されています。やっぱりみんな日常的に SQL 書いてるんですね。

f:id:morishin127:20210610220004p:plain
お客様の声

使用技術

Bdash Server の実装には Blitz.js というフレームワークを使いました。Blitz.js は Next.jsPrisma を内包した Ruby on Rails 風の開発体験を提供するフルスタックフレームワークとなっています。Next.js と Prisma の機能を使うことに加え、rails generate 風なコード生成機能があったり "Zero-API" という思想で作られたデータベースクライアント実装があったりします。"Zero-API" は面白くて、サーバーサイドの実装でもクライアントサイドの実装でも同じように db.user.findUnique({ where: { id: userId }}) といった実装でデータを取得できます。もちろんクライアントサイド(ブラウザ)から直接データベースサーバーが叩けるわけではなくて、ドキュメントによると

You may be wondering how that can work since it's importing server code into your component: At build time, the direct function import is swapped out with a network call. So the query function code is never included in your client code.

とのことでした。この辺りの思想は自分には結構合っていて Blitz.js の開発体験は良かったです。

クックパッドは Ruby on Rails をよく使っているし Rails 風フレームワークを使うなら Rails で良いのではという意見もありそう、かつ僕自身も Rails は好きなのですが、Rails の上でフロントエンドの JavaScript を書こうとするとどうしてもつらい。Webpacker が登場したけどそれでもかなりつらい。個人プロダクトや他所の会社のプロダクトでも幾度となく消耗してきました。消耗しながらフロントエンド実装がしやすいウェブフレームワークは無いものかと思っていた中、社内に Next.js 製のクックパッドのレシピページ実装が登場し Next.js の良さを知り、しかし Rails でいう ActiveRecord 的なもの (DB へのコネクションを管理してくれるやつ & OR マッパー)は必要やねんなと思っていたところ Prisma というのが流行っているのを知り、Next.js + Prisma で何か作ってみるかと思ってたら Blitz.js というのがあるのを知り、今回使ってみた次第でした。

Prisma も開発体験は良くてクエリ実行結果に型が付くし、スキーマ管理も Rails 風にできて使い慣れた感じでした。欲を言えば Rails 風ではなくて ridgepole (社内製のスキーマ管理ツール) になって欲しい。それで言うと ridgepole は Rails 標準になって欲しいと思っていて、Rails 開発をしている方には大変おすすめなので是非使ってみてください。詳しく調べてはいませんが、Primsa はまだ複数データベースサーバーへの接続や Read-write splitting はできなそうに見えました。Rails も複数 DB 接続が標準機能になったのは最近のことですが...。

UI の実装には React と Chakra UI を利用しました。Chakra UI については好みが分かれるところだと思いますが、JSX(TSX) を書きながらそのまま DOM の attribute としてスタイルを宣言していく書き方は高速に UI を構築することができ便利だと思いました。また用意されているコンポーネントも豊富で、簡単にシュッとした見た目に整えることができました。

こんな感じ: https://github.com/bdash-app/bdash-server/blob/267e243a9830f1cb0fff8526858e7feb68cae86b/app/pages/query/%5BbdashQueryIdHash%5D.tsx#L168-L276

Blitz.js もそれを構成する Next.js と Prisma もバリバリ開発されておりどんどん変わっていくので開発が長期に渡る大規模なプロダクトで採用するには不安がありますがこういった小さなアプリで試す分にはかなり開発体験が良く気に入りました。余談ですが Blitz.js は Next.js を fork する案が出ていてちょっと不穏な気配を感じています 😅

github.com

さいごに

お読みいただきありがとうございました。Bdash Server や Bdash クライアントアプリは OSS でありどなたでもご利用可能なので、よければ日々の分析業務にお役立てください。GitHub Issue 上でのご意見・ご要望や Pull Request も歓迎です。

最後にこの記事を読んでくださった分析好きのあなたにおすすめのコンテンツを紹介します。

クックパッドは自らの手でデータを分析しながらサービスを改善していけるエンジニアの仲間を募集しています!応募するほどではないけどちょっと興味があるので社員と話してみたいという方にはカジュアル面談(オンライン)なども実施していますのでお気軽にご連絡ください。雑に @morisin127 に「記事見ました!話を聞いてみたいです」と DM をしていただくので大丈夫です🙆

👇️👇️👇️詳しい採用情報はこちら!👇️👇️👇️

info.cookpad.com

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/