はじめに
Firebase の Cloud Firestore を使ったことがありませんでした。今更な感じですがどんなものか知るために Cloud Firestore を使ってデータの追加・削除・取得をやってみたいと思います。
-------------------------------------------------------------------------------------------
◆ Cloud Firestore とは? ◆
-------------------------------------------------------------------------------------------
Firebase の Cloud Firestore は クラウドホスト型の NoSQLデータベース です。
Cloud Firestore は iOS アプリや Android アプリおよびWebアプリから SDK を介して直接アクセスできます。
-------------------------------------------------------------------------------------------
Firebase のプロジェクトを作成する
Firebase ではプロジェクトを作成し、そのプロジェクトで設定を行うことで Cloud Firestore を利用できるようにします。 なのでまずは次の手順で Firebase のプロジェクトを作成していきます。
- [Firebase] にログインし、「プロジェクト」の作成を選択する。
(FirebaseはGoogleのアカウントでログインできます。) - 「プロジェクト名」に任意の名称を入力して、「続行」を選択する。
- 「Googleアナリティクス」を有効・無効どちらか選択し、「続行」を選択する
- 「Googleアナリティクスアカウント」を選択し、「続行」を選択する
(特に何もなければ、Default Account for Firebase でよいはず) - Firebase のコンソールに移動し、作成した Firebase プロジェクトを選択する。
Android プロジェクトを作成する
Android プロジェクトを作成して、Cloud Firestore を利用できるようにします。
まずば Firebase プロジェクト上で次の操作でセットアップを行います。
- Firebase のコンソールで Firebase プロジェクトを選択する。
- アプリを追加 を選択し、プラットフォーム で Android を選択する。
- Androidパッケージ名の入力が求められるので、Android プロジェクトのパッケージ名を入力し、次へを選択する。
- 設定ファイルのダウンロードで、google-service.json をダウンロードする。
- Firebase SDK の追加 や アプリを実行してインストールを確認は後で確認するので、次へを選択する。
そして Android Studio 上で Android プロジェクトのセットアップを行います。
- [Android Studio]Android プロジェクトを作成する。
- [Andriod Studio]app に google-service.json をコピーする
- [Android Studio]build.gradle で必要なライブラリを加える
buildscript {
ext.kotlin_version = '1.3.71'
repositories {
google() // 追加する
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.3' // 追加する
}
}
allprojects {
repositories {
google() // 追加する
jcenter()
}
}
apply plugin: 'com.google.gms.google-services'
dependencies {
︙
def firebase_analytics_version = "17.3.0"
implementation "com.google.firebase:firebase-analytics:${firebase_analytics_version}"
def firebase_fire_store_version = "21.4.2"
implementation "com.google.firebase:firebase-firestore:${firebase_fire_store_version}"
︙
}
これで Android アプリ から Cloud Firestore にアクセスできるようになります。
Cloud Firestore で Task を追加・削除・取得する
Cloud Firestore は NoSQLデータベースで、ドキュメントデータベースのデータモデルに従ってデータを管理します。データモデルとしてコレクション・ドキュメント・データとありこれを組み合わせてデータを管理します。
データ | |
---|---|
コレクション | 複数のドキュメントを格納し、 データの編集やクエリなどを実行するためのデータモデル |
ドキュメント | データを格納するデータモデル |
データ | 単純な文字列から数値などを格納するデータモデル (サポートしているデータ・タイプ) |
今回は次の Task クラスを定義し、次のデータモデル構成で Task オブジェクトを管理してみたいと思います。
data class Task(val id: String, val time: Long, val name: String) {
companion object {
fun create(name: String): Task = Task(UUID.randomUUID().toString(), Date().time, name)
}
}
データベースを取得する
Cloud Firestore でデータベースを制御するには FirebaseFireStore オブジェクトを利用します。次のコードで FirebaseFireStore オブジェクトを取得します。
private val database : FirebaseFirestore get() = FirebaseFirestore.getInstance()
ドキュメントを追加し、Taskデータを保存する
どのコレクションのドキュメントを追加するか決めます。FirebaseFirestore.collection(name: String)
に名前を入力しコレクションを取得します。コレクションを取得したらCollection.document(name:String)
に任意の名前を入力しドキュメントを作成します。名前を指定しない Collection.document()
でも作成でき、これだとドキュメントの名称は自動で生成されます。
ドキュメントができたらデータを追加するだけですが、データは Map<String, *>
である必要があります。今回は Task.toMap
を用意しました、これで Map<String, *>
に変換して Document.set
してやれば Task オブジェクトを保存できます。
suspend fun add(task: Task): Boolean {
try {
val collection = database.collection("CollectionName")
val document = collection.document(task.id)
val data = task.toHashMap()
document.set(data).await()
return true
} catch (e: Exception) {
return false
}
}
fun Task.toMap(): Map<String, *> {
return hashMapOf(
"id" to this.id,
"time" to this.time,
"name" to this.name
)
}
ドキュメントを削除し、Taskデータを削除する
どのコレクションのドキュメントを削除するか決めます。FirebaseFirestore.collection(name: String)
に名前を入力しコレクションを取得します。コレクションを取得したらCollection.document(name:String)
で削除するドキュメントを取得します。あとはdocument.delete()
を実行するとドキュメントが削除され、そこに格納されていた Task データも削除されます。
suspend fun delete(task: Task): Boolean {
try {
val collection = database.collection("CollectionName")
val document = collection.document(task.id)
document.delete().await()
return true
} catch (e: Exception) {
return false
}
}
ドキュメントを検索し、 ある Task データを取得する
どのコレクションのドキュメントを検索するか決めます。FirebaseFirestore.collection(name: String)
に名前を入力しコレクションを取得します。コレクションにはlimit
やorderBy
などのクエリが用意されており、取得するドキュメントを指定できるようになっています。今回はlimit
を使ってある上限数を以内のドキュメントを取得するようにします。
ドキュメントからデータを取得するだけですが、データは Map<String, *>
であるので、Task
に変換する必要があります。今回は Map<String, *>.toTask
を用意して、これで取得したドキュメントのデータを Task
に変換してやれば Task オブジェクトの取得ができます。
suspend fun fetchTask(limit: Long): List<Task> {
try {
val collection = database.collection("CollectionName")
val documents = collection.limit(limit).get().documents
return documents.map { it.data }.mapNotNull { it?.toTask() }
} catch (e: Exception) {
return listOf()
}
}
fun Map<String, Any>.toTask(): Task {
val id = this["id"] as String
val time = this["time"] as Long
val name = this["name"] as String
return Task(id, time, name)
}
実際に動かしてみる
これでドキュメントの追加・削除・取得ができるようになりました。
これらの処理を TaskRepository にまとめると次のような感じになります。
class TaskRepository {
private val database : FirebaseFirestore get() = FirebaseFirestore.getInstance()
suspend fun add(task: Task): Boolean {
try {
val collection = database.collection(COLLECTION_PATH)
val document = collection.document(task.id)
val data = task.toHashMap()
document.set(data).await()
return true
} catch (e: Exception) {
return false
}
}
suspend fun delete(task: Task): Boolean {
try {
val collection = database.collection(COLLECTION_PATH)
val document = collection.document(task.id)
document.delete().await()
return true
} catch (e: Exception) {
return false
}
}
suspend fun fetchTask(limit: Long): List<Task> {
try {
val collection = database.collection("CollectionName")
val documents = collection.limit(limit).get().documents
return documents.map { it.data }.mapNotNull { it?.toTask() }
} catch (e: Exception) {
return listOf()
}
}
companion object {
private const val COLLECTION_PATH = "tasks"
}
}
次のようにTaskRepository
を使って取得・追加・削除の処理を記述してみましょうか。
class MainActivity : AppCompatActivity() {
private val repository: TaskRepository = TaskRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
// ①現在のタスク一覧を取得する
println("①現在のタスク一覧を取得する ▶ " + repository.fetchTask(100))
// ②新しいタスクを追加する
val newTask = Task.create("New Task")
repository.add(newTask)
println("①新しいタスクを追加する ▶ " + repository.fetchTask(100))
// ③新しく追加したタスクを削除する
repository.delete(newTask)
println("③新しく追加したタスクを削除する ▶ " + repository.fetchTask(100))
}
}
}
実行結果は次のようになりました、取得・追加・削除がうまく動作していますね。
①現在のタスク一覧を取得する ▶ []
②新しいタスクを追加する ▶ [Task(id=8895dfb2-4aa0-4da0-bf3e-72da7a192fb8, time=1586591310782, name=New Task)]
③新しく追加したタスクを削除する ▶ []
おわりに
Firebase Cloud Firestore のチュートリアルではさらっとドキュメントデータベースであることが書かれていますが、ドキュメントデータベースであることがわからないとどれだけ API の仕様を眺めて Cloud Firestore を理解できないですね。NoSQLやドキュメントデータベースのコンセプトがわかると一気に理解できるようになりました。まだデータ構造など何が最適化わからないですが、非常に強力な開発環境だと思うので少しずづ使えるようになっていきたいですね。