コネヒト開発者ブログ

コネヒト開発者ブログ

【Firebase Analytics】新しくなったBigQuery Export まとめ

こんにちは、サーバーサイドのお仕事してます金城(@o0h_)です。
今更ながら忍空2nd STAGE〜干支忍編〜を読みました!黄純いいキャラ。 ここに至る前の話となる、「忍空 7〜9巻」もKindle出ないですかね。。。

忍空―SECOND STAGE 干支忍編― 12 (ジャンプコミックス)

さて、先日「Firebase AnalyticsのBigQuery Exportで、作成されるスキーマが刷新される」という変更が実施されています。
コネヒトでは大変便利にFirebase Analyticsのrawデータを用いた集計・分析を行っていたので、今回の変更は冷静かつ迅速に対応する必要がある内容でした。(以前書いたこの記事とか。)

今回は、「実際どう変わるの」「何を対応すればいいの」といったポイントをまとめたので、折角なので公開しちゃいたいと思います。

https://cdn.mamari.jp/authorized/amana_5b385c1a-8b34-46c0-a57b-0043ac120002.jpg

お品書き

  1. 変更の背景は?
  2. 実際にどう変わるの?新しいスキーマはどうなるの?
  3. 移行に際してやることは?

変更の背景は?

Google I/O 2018で発表された内容として、「Analyticsの強化」というトピックがありました。
今回の仕様変更はそれに伴うアクションの1つであるということが、I/Oの直後にGoogleより届いたメール「Update: Important changes to Firebase BigQuery export」内に記載されていました。

These changes are part of a number of improvements we’re making to Google Analytics for Firebase. Check out the Firebase blog and this YouTube video for a more thorough explanation.
(mailより)

実際にGoogle I/Oの講演の中でBigQuery Exportについて触れているのは、以下の動画の20分過ぎからの部分でしょうか。 youtu.be

しかしながら、ブログ記事中でも講演動画を見ても「じゃあ、どうしてBigQuery Exportで生成されるスキーマまで(こんなにも全面的に)変更されるの?」という部分への具体的な言及は為されていません。
推測するに「プロジェクト レベルのレポート」や「柔軟なフィルタ」の為なのかなとは思います。後述するような、「ユーザーに関する代表的な(フィルタやディメンションとして良く利用されそうな)プロパティ」のスキーマレベルでの高解像度化だったり、イベントレコードの取扱形式の変更だったり・・は、なんとなく「やろうとしていることに対して、データの取り回しを簡単にできるようにする」ものなのかな?という気がしました。

ということで、私は未だ詳しい動機を掴めていないので、 何か詳しい情報をお持ちの方がいらっしゃいましたらソっと教えてください・・・

実際にどう変わるの?新しいスキーマはどうなるの?

ここからが本題です。 スキーマについてはコチラにまとめられています*1
以前のバージョンについては、同ページ内の「Old export schema」に記載があります。

support.google.com

掻い摘んでポイントを挙げると、以下の点でしょうか

  1. iOS/Androidともに、 同じデータセットに 格納されるように
  2. 「ユーザーごと(user_dim)に1行で、イベント(event_dim)が複数ぶら下がる」という構造から「 eventごとに 」格納されるように
  3. 全体的に 階層が浅く なり、一部カラムが追加・命名の見直し

この順番で見ていきたいと思います。

1. データセットの統合 & テーブルの名称の変更

  • BEFORE: アプリごとにデータセットが作成される
  • AFTER: Analytics Property ID ごとにデータセットが作成される

例えば、以下のようなFirebase Analyticsのプロジェクト - アプリ構成があったとします*2

  • Project: my-project
    • Property ID: 123123123
  • App(Bundle ID)
    • Android: com.example.awesomeapp.android
    • iOS: com.example.awesomeapp.ios

これに対して 2018-06-25 時点で作られるであろうスキーマは、旧来であれば以下のような構成でした。

BigQuery Project Dataset Table 内容
my_project com_example_awesomeapp_android app_events_20180625 ANDROIDのデータが格納される
my_project com_example_awesomeapp_ios app_events_20180625 iOSのデータが格納される

アプリごとに別々のDatasetが作成されるため、実際にクエリを叩く際にはUNIONを用いるなどの工夫が必要でした。

新しいスキーマでは、以下のようになります。

BigQuery Project Dataset Table 内容
my_project analytics_123123123 events_20180625 ANDROID/iOSのデータが格納される

これまでの app_events_ というテーブル名が events_ になりした。日付に応じたsuffixが付く、という点には変更ありません。
もっと大きい変更はDatasetの扱いで、同一のpropertyであれば、同じ場所に格納されるようになりました。すなわち、コンソールから確認できるFirebase Analtyicsと同じように扱うことができます。「Androidのアプリだから」「iOSのアプリだから」という分離が無くなるということです。
「どのアプリから来ているレコードか?」は、 app_info.id もしくは stream_id を用いて区別することができます。OS単位での区別には device.operating_system を利用することになります。

2. レコードの構造の変更

  • BEFORE: 1ユーザーに対して一連のイベントをARRAYでまとめて、1つのレコードを作成する
  • AFTER: 1イベントごとに1レコードを作成する

従来は event_dim がREPEATEDになっており、1ユーザー情報に対してARRAYとして集約されていました。新しいスキーマでは、1イベントのログが1行で記録されるようになります。

例えば、以前であればこのように1レコードのevent_dim に配列として複数イベントが格納されています。

f:id:o0h:20180701205946p:plain

新しいスキーマではこのようになります。 Row の振られ方が異なることに注目してください。

f:id:o0h:20180701210504p:plain

これまでイベントに着目した分析や集計を行うにあたっては、スキーマでは配列の内容にアクセスするために複雑なクエリが必要不可欠でした。どうしてもSELECT文がネストされていたり、あるいはFROM句の中にUNNESTを利用した交差結合を用いたり・・・といったものです。
今後は、SELECT COUNT(*) FROM analytics_*** WHERE event_name = "BuyItem" くらいのシンプルなクエリのみで出来ることが増えそうです。

他方で、「一連のイベントがひとまとまりになっている」という状態がなくなるため、ユーザーを主としたシーケンス分析・・のようなものは少し手間が増えるかもしれません。一般的なリレーショナルデータベースと同じく、WINDOW処理やGROUP BYを用いた集計が大事になってきそうですね。

3. 階層構造が浅めに

  • BEFORE : トップ階層に user_dim / event_dim というキーがあり、あらゆる情報がその配下に格納されていた
  • AFTER: ***_dim は撤廃。特に user_dim の配下にまとまっていた情報が分解されている

こちらのgistに新旧のスキーマをjsonに吐き出したものをおいてあります。
これをもとに、「旧来の第2階層(=user_dim, event_dim直下)にあるキー」を括りだすとこのようになりました。

{
    "event_dim": [
        "date",
        "name",
        "params",
        "timestamp_micros",
        "previous_timestamp_micros",
        "value_in_usd"
   ],
   "user_dim": [
        "user_id",
        "first_open_timestamp_micros",
        "user_properties",
        "device_info",
        "geo_info",
        "app_info",
        "traffic_source",
        "bundle_info",
        "ltv_info"
   ]
}

対して、「新しいスキーマの第1階層」はこの様になっています。

[
     "event_date",
     "event_timestamp",
     "event_name",
     "event_params",
     "event_previous_timestamp",
     "event_value_in_usd",
     "user_id",
     "user_pseudo_id",
     "user_properties",
     "user_first_touch_timestamp",
     "user_ltv",
     "device",
     "geo",
     "app_info",
     "traffic_source",
     "stream_id",
     "platform"
]

全体的な構造として、STRUCTUREを意識せずに一般的なSQLっぽい書き方が出来るようになったのではないでしょうか。より「Standard SQL」な世界が近づいてきますね。

とはいえ、名前が変わっているのでとても困ります😠
大まかな対応表をまとめてみました。
※ 一般的に分析用途で用いられそうなカラムに限定しています。

以前のスキーマ 新しいスキーマ 内容(新しいスキーマ)*3
user_dim.user_properties user_properties A repeated record of user properties set with the setUserProperty API.
user_dim.device_info device A record of device information.
user_dim.geo_info geo A record of the user's geographic information.
user_dim.app_info app_info A record of information on the app.
user_dim.traffic_source traffic_source Name of the traffic source used to acquire the user.
user_dim.ltv_info user_ltv A record of Lifetime Value information about the user.
event_dim.date event_date The date on which the event was logged (YYYYMMDD format in the registered timezone of your app).
event_dim.name event_name The name of the event.
event_dim.params event_params A repeated record of the parameters associated with this event.
event_dim.timestamp_micros event_timestamp The time (in microseconds, UTC) at which the event was logged on the client.
event_dim.previous_timestamp_micros event_previous_timestamp The time (in microseconds, UTC) at which the event was previously logged on the client.
event_dim.value_in_usd event_value_in_usd The currency-converted value (in USD) of the event's value parameter.
user_dim.user_id user_id The user ID set via the setUserId API.
user_dim.first_open_timestamp_micros user_first_touch_timestamp The time (in microseconds) at which the user first opened the app.
user_dim.app_info.app_platform platform The platform on which the app was built.
-- stream_id The numeric ID of the stream.

中でも特に特徴的だな、と思ったのは stream_id でしょうか。
これまでなら「1つのアプリ(stream)ごとに、Datasetが作られる」という前提にあったものが「同一のテーブルに複数のstreamが同居する」ようになった、というのがこのフィールドからだけでも読み取れます。

一部、構造の変更の範疇から外れるような命名変更もあることに注意してください。
(例えば、 timestamp_micors 系が timestamp になっていること。traffic_source.user_acquired_campaign -> traffic_source.name など。)

3. 移行に際してやることは?

有り難いことに(恐ろしいことに?)BigQuery Exportを設定しているプロパティについては自然と新スキーマへの切り替えが始まります。
開始のタイミングに差異があるのかは不明ですが、コネヒトでは 20180624から analytics_ Datasetが作成され、旧来のAndroid/iOS別Datasetは app_events_20180626 で作成が停止されました。 操作対象とした期間が新しいスキーマに対応済みの日付なのであれば、そちらを利用していれば問題ないはずです。

旧スキーマにしかデータのない期間の分析だったり、移行期間を跨いでの分析についてはそのままだと支障があります。例えば「6月分の月次集計」などは、このままだと相当難儀しそうです・・・

今回の移行にあたり、Googleがオフィシャルに「旧スキーマの内容を新スキーマに移行するための手順」をまとめくれているので、その内容を紹介したいと思います。

旧スキーマのデータを新スキーマへ移行する

こちらのページに、手順がまとまっています。

Use this script to migrate existing BigQuery datasets from the old export schema to the new one

ざっくりまとめると

  1. Cloud Shellで
  2. 予め用意されたSQLを叩くために
  3. 予め用意されたスクリプトのパラメータを少し書き換えて実行する

という流れです。
複雑なものではないのですが、実行に時間がかかります。
また、紹介されている手順はブラウザ上でのCloud Shellの利用ですが、gcloud CLIからでも問題なく動作すると思います。

Cloud Shell - Browser Based Command Line  |  Google Cloud

先に用意しておくもの

後に実行するシェルスクリプトは、テンプレート内の値を書き換えて実行します。
事前に次の表の内容を控えておいてください

パラメータ 内容 確認場所
PROPERTY_ID Firebase Analyticsのプロパティ 「Firebase Console > Analycis > アナリティクス設定」を開き、画面右上
BQ_PROJECT_ID LinkしているBigQueryのプロジェクト 基本的にはFirebase AnalyticsのProjectと同じID
FIREBASE_APP_ID 送信元のアプリ 「Firebase Console > Project Overview > 画面中部「アプリ」から適当なアプリをクリック」して表示される アプリケーション ID
BQ_DATASET 移行元Dataset BigQuery上の元々のDatasetのID
PLATFORM 移行元データのプラットフォーム IOS もしく ANDROID
START_DATE 移行対象期間の始端
END_DATE 移行対象期間の終端 基本的には、新スキーマにデータが入っていない期間の最終日
スクリプトの準備

実際に、スクリプトを用意していきます。
今回はブラウザ上でCloud Shellを実行するので、スクリプトの作成・保存も対話モードで行ってしまいます。

なお、ここで登場する migration_script.sql migration_script.sh はどちらも先程紹介した記事(Use this script to migrate existing 〜)に記載されているものです。

  1. GCPコンソールから「リソースの管理」を開きます
  2. 一覧の中から、操作対象とするプロジェクトを開きます(プロジェクト名をクリック)
  3. ヘッダーのメニューの中にある「Google Cloud Shellを有効化」をクリック
    • f:id:o0h:20180701221914p:plain
  4. SQLの準備
    • cat > migration_script.sql と入力し、Enterを押します
    • 入力待ちになるので、この状態でmigration_script.sql の内容を入力します
    • ctrl + c を押して、入力待ちを終了します
    • これで sqlが完成しました
  5. シェルスクリプトの準備
    • cat > migration_script.sh と入力し、Enterを押します
    • 入力待ちになるので、この状態でmigration_script.sh の内容を入力します
      • この際に、スクリプト内の各パラメータの内容を変更しておいてください
      • もちろん、一旦保存したあとにCloud Shell上でエディタを起動して変更することも可能です
      • ctrl + c を押して、入力待ちを終了します
      • これで、実行に必要なスクリプトが用意できました
マイグレーションの実行

実行は、Cloud Shell上で先に作成したスクリプトファイルを走らせるだけです

bash migration_script.sh と入力してEnterを叩きましょう!

完了までに多くの時間を要するため不安になるかもしれませんが、気長に待つのが良いと思います。 1日分の実行が終わるたびにCLI上に出力が出るので、動いている様子をご確認ください。

これを、必要な分だけ繰り返して実行します。
プラットフォーム = Android / iOSごとにも別々の実行が必要なので、実行→待機→完了→スクリプト(のパラメータ)の修正→再実行・・・という手順を繰り返してください。

感想

移行アナウンスがされてから、割とサクッと実施日が来てしまったような印象があります・・・w また、これまで書いてきたクエリや集計スクリプトも書き直しだったりもするので、なかなか大変かもな〜というのも正直なところです。

一方で、確かに「イベントごとに引き出す、集計する」というシナリオが多かったので、その辺りのクエリがかなりシンプルに書けるようになったのは有り難い気もします。実際、Redashにあった既存の集計用クエリを書き換えたら見通しが良くなり行数も減りました。 特にBigQueryに不慣れな人や非エンジニアへのハードルが下がりそうという期待は、データ分析の裾野を広げよう〜〜としている立場*4からすると良い話でした✨

コネヒトでもまだまだ対応作業も残っていますが、気持ちを新たに頑張っていこうと思います!

*1:うまく表示されない場合、ページ最下部の表示言語切り替えで「English」を選択してください

*2:※ Property ID は、 console.firebase.google.com > アナリティクス > アナリティクス設定から確認できます。

*3:https://support.google.com/firebase/answer/7029846?visit_id=1-636656596224100576-4160687049&rd=1&hl=en より

*4:http://tech.connehito.com/entry/2018/04/03/184433