FirestoreをCloud Functionsで試してみた

初めてFirestoreを触ります。
2023.12.30

クラスメソッド株式会社データアナリティクス事業本部所属のニューシロです。
今回はGoogle CloudのサービスであるFirestoreを同じくGoogle CloudのサービスであるCloud Functionsを用いて試してみました。

前提

Firestoreとは

概要

初めて触れるFirestoreについて、あらかじめ調査しておきます。
公式ドキュメントを読み進めながら気になったところをピックアップしてみます。

Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQL ドキュメント データベースです。

私が普段よく使っているBigQueryとは違い、NoSQLのデータベースです。

Firestore は NoSQL ドキュメント指向データベースです。SQL データベースとは違い、テーブルや行はありません。代わりに、データは「ドキュメント」に格納し、それが「コレクション」にまとめられます。
各「ドキュメント」には、一連の Key-Value ペアが含まれています。

Firestoreにはドキュメントコレクションという概念があります。 それぞれ見ていきましょう。

ドキュメント

Firestore では、ストレージの単位はドキュメントになります。ドキュメントは、値にマッピングされるフィールドを含む軽量のレコードです。各ドキュメントは名前で識別されます。

データはドキュメントとしてFirestoreに格納されます。
引用元の公式ドキュメントにありますが、ドキュメントはJSONと似ており、実際基本的にはJSONと同じようです。

見てわかるように、ドキュメントは JSON によく似ており、実際基本的には JSON と同じです。いくつかの違いはありますが(たとえばドキュメントでは追加のデータ型がサポートされており、サイズは 1 MB までに制限されています)、一般的にドキュメントは軽量の JSON レコードとして扱うことができます。

コレクション

ドキュメントはコレクションの中にあります。コレクションは端的に言えばドキュメントのコンテナです。たとえば、users コレクションを作成して、さまざまなユーザーを表すドキュメントを格納できます。

コレクションで、先ほどのドキュメントを格納できます。
コレクションは、ドキュメントを格納するフォルダのような概念と考えてよさそうです。

コレクションとドキュメントは Firestore で暗黙的に作成されます。ユーザーはデータをコレクション内のドキュメントに割り当てるだけです。コレクションまたはドキュメントのいずれかが存在しない場合は、Firestore によって作成されます。

BigQueryのようにデータセットやテーブルを作成せずとも、Firestoreはコレクションとドキュメントを自動で作成してくれるようです。 この機能については後ほど本記事で実際に挙動を確認しています。

Cloud Functionsとは

Google Cloud Functions は、クラウド サービスの構築と接続に使用するサーバーレスのランタイム環境です。Cloud Functions を使用すると、クラウドのインフラストラクチャやサービスで生じたイベントに関連する、シンプルで一義的な関数を作成できます。対象のイベントが発生すると、Cloud Functions がトリガーされ、コードがフルマネージドの環境で実行されます。

Cloud Functionsを用いると簡単にGoogle Cloud上でPythonコードが実行できます。
勿論Cloud Functions以外でもFirestoreは利用できますが、今回はCloud Functionsを使っています。

本題

Firestore実装

データベース作成

初めての利用でしたのでまずはFirestoreのAPIを有効にしました。
その後、Firestoreでデータベースの作成を実行します。

モードはネイティブモードを選択します。
データベースにDatastoreとの下位互換性を持たせるならDatastoreモードを選択するようですが、今回は必要ありません。

データベースIDは(default)で作成します。
無料の割り当てが利用できるのは(default)データベースのみのようです。

アプリで複数のデータベースが不要な場合は、(default) データベースを使用します。
データベースを指定しない場合、Firestore クライアント ライブラリと Google Cloud CLI はデフォルトで (default) データベースに接続されます。
無料の割り当ては、(default) データベースでのみ使用できます。

セキュリティに関しては、開発段階なのでテストルールでも良いかと思いましたが、今回は本番環境ルールで設定しました。
データベースを作成をクリックします。

(default) データベースが作成できました。

権限調査

公式ドキュメントから権限を調査します。 今回はroles/datastore.userがあれば問題なさそうです。Datastoreと権限は共通のようです。

Cloud Functions実装

環境

今回利用したCloud Functionsの環境です。

  • 第2世代
  • Pub/Subトリガー
  • Python3.11

Pub/Subトリガーを用いるため、あらかじめトリガー元のPub/Subトピックも作成しておきます。

サービスアカウントの権限

Cloud Functionsで使用したサービスアカウントには、先ほど調査したroles/datastore.user(Cloud Datastore ユーザー)を付与してあります。

コード

下記が実装したコードです。公式ドキュメントを参考にしました。
受信したPub/Subメッセージの情報をFirestoreへ書き込むコードです。

main.py

import base64
import os

import functions_framework
from google.cloud import firestore

MY_PROJECT_ID = os.getenv("MY_PROJECT_ID")  # 環境変数からプロジェクト名を取得


@functions_framework.cloud_event
def write_to_firestore(cloud_event):
    # Pub/Subのメッセージ情報を取得
    message_data = cloud_event.data["message"]

    # Firestoreへ追加するPub/Subのデータ
    data = {
        "message_id": message_data["message_id"],
        "publish_time": message_data["publish_time"],
        "message": base64.b64decode(message_data["data"]).decode()
    }

    # Firestoreへデータ追加
    db = firestore.Client(project=MY_PROJECT_ID)
    doc_ref = db.collection("cloud_functions").document("test_document")
    doc_ref.set(data)

requirements.txt

functions-framework==3.*
google-cloud-firestore

コード内のハイライト部分でFirestoreへデータを追加しています。
プロジェクト名は環境変数で指定しています。

コレクションIDはcloud_functionsに指定しました。
ドキュメントIDは今回test_documentを指定しましたが、指定しない場合は自動的にランダムなIDを作成してくれます。

では、Cloud Functionsを実行してみましょう。

Cloud Functionsを実行してFirestoreにデータを書き込む

トリガー元のPub/SubトピックにTest message!とメッセージを送ります。
Cloud Functionsが起動したことを確認し、Firestoreを見てみましょう。

コレクションcloud_functionsが自動作成され、またドキュメントtest_documentも作成されています。
Pub/Subメッセージの情報も書き込まれています。
以上でCloud Functionsを用いたFirestoreへの書き込みができました!

最後に感想

思ったより簡単にFirestoreを試すことができて驚きました。
コレクションやドキュメントの設計が難しそうだなと感じたので引き続き調べていこうと思います。

引用・参照まとめ