3行で
- Firebase Extensionsや、Cloud SchedulerでバックアップしたFirestoreのデータではなく、特定のリクエストをトリガーにBigQueryへデータを追加したかった
- 自分で作成したデータセット、テーブル、スキーマにデータを追加できるので分析がしやすくなりました
- BigQueryのドキュメントがなかなか見つからず辛かったので記事にしました
実装のきっかけ
今年の9月に月額課金をもつサービスをリリースしました1。
このサービスのWebの解約画面に「解約理由」を必須にして欲しいと要望がありました。
「解約理由」は次の要件でした。
- チェックボックス形式で解約理由の項目を選択できる(必須かつ複数選択可)
- テキスト形式で自由に解約の理由について書くことが出来る(任意)
- それぞれの解約理由の合計を知りたい
実際に開発した解約理由の画面 |
---|
Cloud FunctionsからBigQueryへデータを送信する手順
1. Cloud Functionsのコード
BigQueryを準備する前に、先にコードを見せた方が理解しやすいと思うのでコードを載せました。
解約処理は、本記事には関係ないので「解約理由をBigQueryに送信する」コードを中心にして書き換えてます。
余談ですが、 BigQueryのドキュメントを探すのが一番苦労したのでコードのコメントにも追記しています。
探しにくいと感じるのは僕だけでしょうか 😭
import * as functions from 'firebase-functions';
import { BigQuery } from '@google-cloud/bigquery';
interface CancelReason {
id: number;
title: string;
}
/*
* 月額プランを解約する
*/
export = functions
.region('asia-northeast1')
.https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError(
'unauthenticated',
'認証エラー',
data
);
}
if (!data.reasons || data.reasons.length === 0) {
throw new functions.https.HttpsError(
'invalid-argument',
'解約の理由を選択してください',
data
);
}
try {
// ここで解約(処理は省略)
await sendBigQuery(context.auth.uid, data);
} catch (error) {
throw new functions.https.HttpsError(error.code, error.message, data);
}
});
/*
* 解約理由をBigQueryに送信する
* doc: https://cloud.google.com/nodejs/docs/reference/bigquery/3.0.x/Table#insert
*/
async function sendBigQuery(
uid: string,
data: { reasons: CancelReason[]; comment: string }
) {
try {
const bigQuery = new BigQuery({ projectId: process.env.GCLOUD_PROJECT });
const table = bigQuery.dataset('データセットID').table('cancel_payment');
const isExists = await table.exists();
if (!isExists[0]) {
console.error(`🧨 sendBigQuery: cancel_paymentテーブルがない`);
return;
}
const ids: number[] = data.reasons.map(v => v.id);
await table.insert({
TIMESTAMP: bigQuery.timestamp(new Date()),
UID: uid,
DATA: JSON.stringify(data),
COMMENT: data.comment,
REASON_1: ids.includes(1) ? 1 : 0,
REASON_2: ids.includes(2) ? 1 : 0,
REASON_3: ids.includes(3) ? 1 : 0,
REASON_4: ids.includes(4) ? 1 : 0,
REASON_5: ids.includes(5) ? 1 : 0,
REASON_6: ids.includes(6) ? 1 : 0,
});
} catch (e) {
console.error(`🧨 sendBigQuery: ${JSON.stringify(e)}`);
throw new functions.https.HttpsError(
'internal',
'解約処理中にエラーが発生しました'
);
}
}
2. BigQueryでデータセット、テーブルを作成する
const bigQuery = new BigQuery({ projectId: process.env.GCLOUD_PROJECT });
const table = bigQuery.dataset('データセットID').table('cancel_payment');
上記のとおり、連携したFirebaseのプロジェクトのBigQueryにデータセットとテーブルを作成します。
要件どおりにスキーマの型や説明を加えて分析しやすくします。
BigQueryにデータセットとテーブルを作成 | スキーマ |
---|---|
データセット、テーブル、スキーマの各ドキュメントは下記のページにあります。
- https://cloud.google.com/bigquery/docs/datasets-intro?hl=ja
- https://cloud.google.com/bigquery/docs/tables-intro?hl=ja
- https://cloud.google.com/bigquery/docs/schemas?hl=ja
3. クエリで分析する
1.のコードをデプロイして解約理由がBigQueryに送信されてるかクエリを実行して確かめます。
SELECT
count(uid) AS cancel_count,
COUNT(CASE
WHEN reason_1=1 THEN reason_1
ELSE
NULL
END
) AS reason_1,
COUNT(CASE
WHEN reason_2=1 THEN reason_2
ELSE
NULL
END
) AS reason_2,
COUNT(CASE
WHEN reason_3=1 THEN reason_3
ELSE
NULL
END
) AS reason_3,
COUNT(CASE
WHEN reason_4=1 THEN reason_4
ELSE
NULL
END
) AS reason_4,
COUNT(CASE
WHEN reason_5=1 THEN reason_5
ELSE
NULL
END
) AS reason_5,
COUNT(CASE
WHEN reason_6=1 THEN reason_6
ELSE
NULL
END
) AS reason_6
FROM
`プロジェクト名.データセット名.cancel_payment`
クエリの結果 |
---|
実装時の注意点
BigQueryのデータセットやテーブルを作成し直したり、スキーマを変更しても、即座に変更が反映されるわけではないようです。
最大でも1分ぐらい待ってから、Cloud Functionsの関数にリクエストを送った方が期待した結果が返ってきます。
BigQuery側のデータを変更したとき、たまに正常に動かない時があったので知っておくと良いかもしれません。
おわりに
やってることはドキュメントを見れば理解できるので簡単な実装です。
しかし、それ故に自分と同じことを考える記事を見かけませんでした。
僕の検索力が低いだけかもしれないですが。
ただ、解約理由の要件が複数選択ではなく一つだけ選択の場合、Google Analyticsにデータを送信する方が圧倒的に楽です。
もし、このような複数選択のデータでも、Google Analyticsで簡単に分析できる方法を知っている人がいれば、記事にしてほしいです 🥺
コードを書く前に、最小工数で要件を満たせるよう、FirebaseやBigQueryを使い倒していきたいですね 😄