Location Tech Advent Calendar 2021 by LBMA Japan 🛰 Advent Calendar 2021の1日目ということで、Firestoreのデータベースに位置情報データを投入するというごく基本的な記事を書いていきます!
対象
Firebase Firestoreを使い始めた人
位置情報データを扱いたい人
背景
Firebase Firestoreは、JSONのようなスキーマでデータを出し入れできるGoolge Cloud Platformのデータベースで、極めて高速・柔軟にデータの処理ができます。
一方で、制約条件も多くあります。例えば、Firestoreに登録されたデータを2つ以上のフィールドを用いて検索することは基本的にはできません。SQLでいうと、「Where句で2つ以上のフィールドを使った条件分が書けない」というようなイメージです。このようなリレーショナル・データベースだとあまり考えられないような制約条件が、Firebaseには他にもいくつかあります。
こうした制約条件は一見厳しいように見えますが、通常の場合、クライアント側で解決が可能です。例えば、「1つのフィールドしか検索できない」という制約条件については、「1つのフィールドで検索して、結果をクライアント側に持ってきて、クライアント側でより細かく絞る」ということが可能なので、大きな問題にはなりません。
しかし、位置情報データを取り扱う場合には、大きな問題が生じます。というのは、緯度と経度で定義される「位置情報」は、「緯度だけ」や「経度だけ」のように、片方だけで利用するケースがほぼ無いからです。仮に緯度だけで絞ったとしても、ものすごくたくさんのデータがヒットしてしまい、たいてい、クライアントでの処理が大変遅くなってしまいます。つまり、Firebaseで緯度と経度を
というように登録してしまうと、パフォーマンス上色々問題が出てしまいます。
そこで、FirestoreではGeoPoint
型と呼ばれる型を導入することで、この問題に対処しています。これは、緯度と経度が一緒になったフィールドです。 緯度と経度を同時に持つので、GeoPoint
型で検索すれば、Firestoreの「1つのフィールドでしか検索できない」制限をクリアすることができるように思えます。
しかし、ここでも1つ問題があります。それは、FirestoreではGeoPoint
型を直接指定してクエリを打つことができないという問題です。例えば、「このGeoPoint
型のデータの半径50m以内のドキュメントをもってきてください」というようなことが、現状のFirestoreではできません。そのため、ちょっとした工夫が必要になります。
こうした点を背景に、以下では、Python Firebase Admin SDKを用いた位置情報データの登録方法について解説していきます。検索方法については次の記事にて。
位置情報データの登録
バージョン情報
本記事では以下のバージョンで実施しています。
[tool.poetry.dependencies]
python = "^3.9"
firebase-admin = "^5.0.3"
google-cloud = "^0.34.0"
型やクライアントの準備
主として利用するのはFirebase Admin SDKですが、型ヒント(type-hint)を使うために、google-cloud側のfirestoreライブラリも利用します。
from firebase_admin import initialize_app, firestore
from google.cloud.firestore import AsyncDocumentReference, AsyncCollectionReference, GeoPoint
initialize_app(options={"projectId":"Your_GCP_PROJECT_ID"})
db_client = firestore.AsyncClient()
コレクションの定義
コレクションへのリファレンスを定義します。ここではlocationsというリファレンスを作ります。
locations_ref: AsyncCollectionReference = db_client.collection('locations')
これで準備は終了です。
位置情報データの追加
最後の位置情報データを追加する処理について書きます。locationsコレクションにgeopointというフィールドを持つドキュメントを追加していきます。
# GeoPoint型のデータをもつdataを定義
data = {"geopoint": GeoPoint(38.1233, 139.7548968)}
# 追加
result_doc_ref: AsyncDocumentReference
_,result_doc_ref = await locations_ref.add(data)
print(result_doc_ref.to_dict()['geopoint'].latitude)
# >> 35.7018944
格納した結果(値は異なっていますが)
上記の手順により、Firestoreに追加することはできました。しかし、背景で説明したとおり、FirebaseにはGeoPointで検索をする機能がありません。そこで、実用的に使うためには、もうひと工夫が必要です。こうした検索の工夫については、次の記事で書きますね。
参考
firestore client 公式ドキュメント
https://googleapis.dev/python/firestore/latest/client.html