とりゅふの森

GCPデータエンジニアとして生きる

【超入門】Cloud FirestoreをPythonで操作する

f:id:true-fly:20210902230935p:plain

前回はGCPのNoSQLサービス、Cloud Firestoreの使い始め、GUIでの操作方法をについてご紹介しました。
www.true-fly.com

本日のテーマはこちら!

GCPのNoSQLサービス、Cloud FirestoreをPythonで操作してみよう

今回はプログラムからCloud Firestoreのデータを操作したり検索したりして、自分たちのアプリケーションに導入できるように進めていこうと思います!

準備

ディレクトリ構成

以下のディレクトリ構成で検証を進めていきます。
main.pyがPythonプログラム、
service_account.jsonがサービスアカウントの認証情報です。

.
├── main.py
└── service_account.json

サービスアカウントの設定をする

Cloud Firestoreに限らず、プログラムでGCPのプロダクトを操作するにはサービスアカウントが必要です。
まずはサービスアカウントを作成します。 GCPコンソールから、[IAMと管理] > [サービスアカウント] > [サービスアカウントを作成]と進みます。

console.cloud.google.com

名前や説明は任意です。

サービスアカウントに「Cloud Firestore 編集者」ロールを付与します。

その後、認証情報(秘密鍵)をservice_account.jsonというファイル名でローカルに保存しておきます。

ライブラリのインストール

pipで以下のライブラリをインストールしておきます。

pip install google-cloud-firestore

環境変数の設定

service_account.jsonがあるディレクトリで以下を実行します。プログラム上で参照する環境変数です。

export PROJECT_ID=自分のGCPのプロジェクトID
export GOOGLE_APPLICATION_CREDENTIALS="$PWD/service_account.json"

コレクションを作成する

Cloud FirestoreのWebUIで予めコレクションを作成しておきます。
今回はtrue-flyというコレクションを作成しました。
最低ドキュメントが1つ必要なので、空で作成しています。

f:id:true-fly:20210902222804p:plain

ドキュメントを作成する

true-flyコレクションにドキュメントをPythonで作成していきます。
db.collection('true-fly') で、true-flyというコレクションを指定しています。
7行目のdoc_ref.set()で、dictをパラメータに入れて実行すると、Cloud Firestoreにドキュメントが追加されます。
今回は当ブログの記事のRSSデータを登録してみます。

import os
from google.cloud import firestore

db = firestore.Client(project=os.environ['PROJECT_ID'])

doc_ref = db.collection('true-fly').document()
doc_ref.set(
    {
        'title': '【超入門】GCPのNoSQL、Cloud Firestoreを使ってみる',
        'url': 'https://www.true-fly.com/entry/2021/09/01/070000',
        'published_at': '2021-09-01 07:00:00',
        'categories': ['GCP', 'Cloud Firestore'],
    }
)

結果をWebUIで確認します。無事データが入りました!

f:id:true-fly:20210902223224p:plain

ドキュメントのIDは自動で割り振られます。IDを自分で指定したいときは、db.collection('true-fly').document('ここでIDを指定')で設定できます。

import os
from google.cloud import firestore

db = firestore.Client(project=os.environ['PROJECT_ID'])

doc_ref = db.collection('true-fly').document(’00001’)
doc_ref.set(
    {
        'title': 'データエンジニアなら知っておきたい、NoSQLってなに?',
        'url': 'https://www.true-fly.com/entry/2021/08/31/070000',
        'published_at': '2021-08-31 07:00:00',
        'categories': 'IT読み物'
    }
)

f:id:true-fly:20210902224330p:plain

ドキュメントの一覧を取得する

ドキュメント一覧を取得したい場合は、db.collection('true-fly').stream()で取得できます。配列で返ってくるので、forで回して、1行ずつdictに変換して標準出力してみます。

import os
from google.cloud import firestore

db = firestore.Client(project=os.environ['PROJECT_ID'])

docs = db.collection('true-fly').stream()

for doc in docs:
    print(doc.to_dict())

結果

$ python main.py 
{'published_at': '2021-08-31 07:00:00', 'categories': 'IT読み物', 'url': 'https://www.true-fly.com/entry/2021/08/31/070000', 'title': 'データエンジニアなら知っておきた
い、NoSQLってなに?'}
{'categories': 'IT読み物', 'url': 'https://www.true-fly.com/entry/2021/08/31/070000', 'title': 'データエンジニアなら知っておきたい、NoSQLってなに?', 'published_at': '2021-08-31 07:00:00'}
{}

ドキュメントを検索する

当ブログのRSSデータをある程度追加した上で、今度はドキュメントを検索してみます。 f:id:true-fly:20210902225342p:plain:w480

ドキュメントを検索するには、db.collection('true-fly').where(演算子).stream()でできます。
例えば文字列の完全一致検索であれば以下のとおりです。

import os
from google.cloud import firestore

db = firestore.Client(project=os.environ['PROJECT_ID'])

docs = db.collection('true-fly').where('title', '==', 'Airflow Breezeを使い、Windows10上でAirflowを動かす').stream()

for doc in docs:
    print(doc.to_dict())

結果

$ python main.py 
{'url': 'https://www.true-fly.com/entry/2021/07/18/180000', 'published_at': '2021-07-18 18:00:00', 'title': 'Airflow Breezeを使い、Windows10上でAirflowを動かす', 'categories': ['AIrflow', 'Python']}

クエリ演算子はこちらのドキュメントに記載されています。

cloud.google.com

配列内の要素で検索は、array_contains演算子でできました。

import os
from google.cloud import firestore

db = firestore.Client(project=os.environ['PROJECT_ID'])

docs = db.collection('true-fly').where('categories', 'array_contains', 'Python').stream()

for doc in docs:
    print(doc.to_dict())

結果

$ python main.py 
{'categories': ['GCP', 'Cloud Functions', 'Python'], 'title': '【Google Cloud Functions】Pythonでサーバレスサービスに入門してみる【FaaS】', 'published_at': '2021-08-25 07:00:00', 'url': 'https://www.true-fly.com/entry/2021/08/25/070000'}
{'url': 'https://www.true-fly.com/entry/2021/08/23/073000', 'published_at': '2021-08-23 07:30:00', 'categories': ['Python入門', 'Python'], 'title': '【Python入門】コメ
ントとdocstring'}
{'title': 'Airflow Breezeを使い、Windows10上でAirflowを動かす', 'url': 'https://www.true-fly.com/entry/2021/07/18/180000', 'published_at': '2021-07-18 18:00:00', 'categories': ['AIrflow', 'Python']}
{'title': '【Cloud Functions】Cloud Schedulerを使って、サーバレス関数をスケジュール実行する【FaaS】', 'categories': ['GCP', 'Cloud Functions', 'Python'], 'published_at': '2021-08-26 07:00:00', 'url': 'https://www.true-fly.com/entry/2021/08/26/070000'}

文字列の部分一致のような高度なクエリは実行できないようです。このへんがNoSQLの弱みかもしれません。

まとめ

以上、今日はCloud FirestoreをPythonで操作する方法についてご紹介しました。
データの登録はスキーマレスなのでdictで自由に登録できたり、全件データを取得するのも関数一つでできるので、複雑なコードを書かなくともデータの出し入れができるのが非常に魅力的です。
次回はGCPのサーバレスサービス、Cloud FunctionsでCloud Firestoreに接続し、サーバレスでデータの読み込み、書き込みを実現する方法についてご紹介しようと思います!