こんにちは、エクスチュアの權泳東(権泳東/コン・ヨンドン)です。
今日はFirebase Analyticsのデータについてです。
Firebase AnalyticsをBigQueryエクスポートすると、user_dimとevent_dimの2列だけのスキーマになりますよね。
Google Analyticsのデータもそうですが、JSONのようなネストになったレコードで構成されてます。
これらのネストになったデータを、サードパーティや自前のデータウェアハウスやデータベースにインポートしようとするなら、FLATTEN関数やUNNEST関数をつかってフラットな1行に展開して、つまりCSVデータに変換するようなETLを挟むと扱いやすくなります。
参考になるのは公式ブログですね。
BigQuery 活用術: UNNEST 関数
標準SQLのUNNEST関数を使う例。
あとは、レガシーSQLのFLATTEN関数を使う例もこちらのブログに載ってます。
Using BigQuery and Firebase Analytics to understand your mobile app
しかし私はどうもSQLじゃなくて他のプログラムで片付けるのが好みなので、Compute Engine上でNode.jsを使ってフラットなCSVデータに変換してみました。
BigQueryからJSON形式でCloud Storageにエクスポートする
まずはbqコマンドを使ってJSON型で抽出します。
$ bq extract --destination_format NEWLINE_DELIMITED_JSON foobar:firebasetest_ANDROID.app_events_20170702 gs://hoge/firebase/test20170702.json
Cloud StorageからCompute Engineにコピーする
次に、gsutilコマンドを使ってcompute engineのインスタンスにコピーします。
$ gsutil cp gs://hoge/firebase/test20170702.json ./
ネストされたJSONをフラットなCSVに変換
Node.jsで簡単なコードを書いて実行します。
JSONファイルをReadStreamで読み込みながら、ネストになったJSONレコードをフラットに1行にして、WriteStreamに書き出して行きます。
var fs = require("fs");
var rs = fs.createReadStream("test20170702.json");
var readline = require("readline");
var rl = readline.createInterface(rs, {});
var of = "test20170702.csv";
var ws = fs.createWriteStream(of, "utf-8");
rl.on("line", function(json) {
var data = JSON.parse(json);
//user_dimの展開
var first_open_timestamp_micros = data.user_dim.first_open_timestamp_micros ? data.user_dim.first_open_timestamp_micros : "";
var device_category = data.user_dim.device_info.device_category;
var mobile_brand_name = data.user_dim.device_info.mobile_brand_name;
var mobile_model_name = data.user_dim.device_info.mobile_model_name;
var mobile_marketing_name = data.user_dim.device_info.mobile_marketing_name;
var device_model = data.user_dim.device_info.device_model;
var platform_version = data.user_dim.device_info.platform_version;
var resettable_device_id = data.user_dim.device_info.resettable_device_id;
var user_default_language = data.user_dim.device_info.user_default_language;
var limited_ad_tracking = data.user_dim.device_info.limited_ad_tracking;
var continent = data.user_dim.geo_info.continent;
var country = data.user_dim.geo_info.country;
var region = data.user_dim.geo_info.region;
var city = data.user_dim.geo_info.city;
var app_version = data.user_dim.app_info.app_version;
var app_instance_id = data.user_dim.app_info.app_instance_id;
var app_store = data.user_dim.app_info.app_store;
var app_platform = data.user_dim.app_info.app_platform;
var app_id = data.user_dim.app_info.app_id;
var bundle_sequence_id = data.user_dim.bundle_info.bundle_sequence_id;
//event_dimの展開
for (var i = 0; i < data.event_dim.length; i++) {
var date = data.event_dim[i].date;
var event_name = data.event_dim[i].name;
var timestamp_micros = data.event_dim[i].timestamp_micros;
var firebase_screen = "";
var firebase_screen_class = "";
var engagement_time_msec = "";
for (var j = 0; j < data.event_dim[i].params.length; j++) {
if (data.event_dim[i].params[j].key == "firebase_screen") {
firebase_screen = data.event_dim[i].params[j].value.string_value;
} else if (data.event_dim[i].params[j].key == "firebase_screen_class") {
firebase_screen_class = data.event_dim[i].params[j].value.string_value;
} else if (data.event_dim[i].params[j].key == "engagement_time_msec") {
engagement_time_msec = data.event_dim[i].params[j].value.int_value;
}
}
ws.write(first_open_timestamp_micros + ",");
ws.write(device_category + ",");
ws.write(mobile_brand_name + ",");
ws.write(mobile_model_name + ",");
ws.write(mobile_marketing_name + ",");
ws.write(device_model + ",");
ws.write(platform_version + ",");
ws.write(resettable_device_id + ",");
ws.write(user_default_language + ",");
ws.write(limited_ad_tracking + ",");
ws.write(continent + ",");
ws.write(country + ",");
ws.write(region + ",");
ws.write(city + ",");
ws.write(app_version + ",");
ws.write(app_instance_id + ",");
ws.write(app_store + ",");
ws.write(app_platform + ",");
ws.write(app_id + ",");
ws.write(bundle_sequence_id + ",");
ws.write(date + ",");
ws.write(event_name + ",");
ws.write(timestamp_micros + ",");
ws.write(firebase_screen + ",");
ws.write(firebase_screen_class + ",");
ws.write(engagement_time_msec + "\n");
}
});
簡単なテストアプリのサンプルデータなので、カスタムのイベントとプロパティのデータの処理は入ってません。
なので実際はプロパティの数だけカラムは増えます。
あとはset_timestamp_usecやら、previous_timestamp_microsなど他にもデータはあるのですが、そこらへんは今回は省略してます。
このNode.jsを実行すると、このようなCSVデータになりました。

ネストされてないフラットなCSVなので、各データウェアハウスやデータベースに取り込みやすくなりました。
弊社ではFirebase Analyticsの他、Google AnalyticsやAdobe AnalyticsのデータをGoogle Cloud Platformを用いて分析する業務支援を行なっております。
お問い合わせはこちらからどうぞ。
ブログへの記事リクエストはこちらまで












