🐥

FirestoreをBigQueryで分析したい

2022/03/11に公開

概要

  • __key__.name__key__.path を使えばFirestoreのDB構造を自由にSQLで扱えるよという話

背景

Firestoreはめっちゃ便利なデータベースなんですが、「〜毎の回数」や「設定が〇〇かつXXのユーザー」をぱっと見たい時はどうしても難しいですよね。

こんなときほとんどの方はこのどちらかの方法で集計・分析を行なっているのではないでしょうか?

  1. BigQueryにロードする
  2. 何かしらのコード・スクリプトを書く

BigQueryとWeb SDK(JavaScript)の書き方例を書いてあるので、「このデータの時ってどうやって取り出せばいいんだろう」という時に見てもらえれば嬉しいです!

FirestoreをBigQueryにロードする

今回はこちらの紹介は省きますが、BigQueryにロードするには大きく2つの方法があります。Export/Import機能を使ってコレクションを丸ごとロードする、トリガーを使ってストリーミングする(Extensionになりました🎉)

ロードしたテーブルがどうなるかみてみる

Firestoreをロードすると、デフォルトで __key__ というフィールドが作られます。

  • 実際のデータ(イメージ)
__key__.name __key__.path
62FnzlzrjNYMGZ2d9zt2 "users", "62FnzlzrjNYMGZ2d9zt2"
85R85YA5QUjb2R6eTeVT "users", "85R85YA5QUjb2R6eTeVT"
GhCTv4jNW4yJQq3E0XHO "users", "GhCTv4jNW4yJQq3E0XHO"

大事なのは2つです

  • __key__.name・・・FirestoreにおけるドキュメントID
  • __key__.path・・・Firestoreにおけるコレクションパス

サブコレクションの場合は、 __key__.path の中身を取り出すことで、「親のコレクション名」や「親のドキュメントID」も使うことができます!!!

実際のクエリ

ドキュメントIDを指定してデータを取得する

  • Firestore
/users/{userId}
  • Web SDK(ver9)
import { doc, getDoc } from "firebase/firestore";
const doc = await getDoc(doc(db, "users", "userId"))
  • BigQuery
select * from firestore.users where __key__.name = 'userId'

クエリを発行してデータを取得する

  • Firestore
/users/{userId}
  • Web SDK(ver9)
import { collection, query, where, getDocs } from "firebase/firestore";
const docs = await getDocs(query(collection(db, "users"), where("prefecture", "==", "Tokyo")));
  • BigQuery
select * from firestore.users where prefecture = 'Tokyo'

サブコレクションに対してクエリを発行する

  • Firestore
/users/{userId}
/users/{userId}/posts/{postId}
  • Web SDK(ver9)
import { collection, doc, setDoc } from "firebase/firestore";

const user = await getDoc(doc(db, "users", "userId"))
if(user.exists) {
  const posts = await getDocs(query(collection(db, "users", user.id))
}
// ※コレクショングループクエリでも可能
  • BigQuery
select * from firestore.users
left join firestore.posts
on SPLIT(REPLACE(posts.__key__.path, '"', ''), ', ')[SAFE_OFFSET(1)] = users.__key__.name

!!!!急に見慣れないSQLが出てきましたね
これが今回のメインテーマでもある「自在にFirestoreのデータを取り扱う」ポイントです!
サブコレクションの場合、BigQueryにはこんな感じで文字列として入っています
"親コレクションの名前", "親ドキュメントのID", "自分のコレクションの名前", "自分のドキュメントのID"

そのため、以下の流れで「親ドキュメントのIDのみ」だけを取り出しています

  1. シングルコーテーションを削除する
  2. 文字列をカンマ区切りで配列にする
  3. 配列の2番目(ID)を取り出す

これを応用すれば「サブコレクションの中でも親コレクションが〇〇のものだけに限定する」や「ネストされたコレクションのSelectに全て親のデータをJOINする」といったケースでも柔軟に対応できます!

おまけ

  • 一番上のコレクションの名前とドキュメントIDを返すUDF
CREATE TEMPORARY FUNCTION
  get_parent (path STRING ) AS ( STRUCT(SPLIT(REPLACE(path, '"', ''), ', ')[SAFE_OFFSET(1)] AS id,
      SPLIT(REPLACE(path, '"', ''), ', ')[SAFE_OFFSET(0)] AS collection_name) );

参考

Discussion