AIエンジニアが一ヶ月でアプリ開発12:データベースの設計②
こんにちは、りぼっちです。
今回は、前回やったデータベース設計の続きです。
Firestore のリファレンスとは
Firestore におけるリファレンスとは、どうやら対象データの場所(パス?)を示すものらしい。ちょっと勘違いしていたのだが、どうやら勝手にインスタンス的なものが生成されたりはしないようだ。
外部キー的なもので join したような形で使えるのかと思っていたけど、そんなに便利なものでもないらしい。
Firestore の課金体系
Firestore は DB なので保存している容量によって課金される。一方で、クラウドサービスでもあるので、リクエストの回数によっても課金される仕組みとなっている。(リクエストのサイズなど通信量による料金の違いは無いらしい)
なので、できるだけリクエストの回数を減らすような設計にするのが望ましい。
リファレンス型は、その場所にある DocumentReference が得られるが中身の情報を得るためには リファレンスごとに get() してやる必要があるようだ。
一方で、もう一つ考えていた、project ごとに task の subCollection を持つ方法だと、project単位ではあるが task が一気に取ってこれるが、たとえば、全ての task から特定の task だけを検索することは出来ないらしい。
試しに、以下のようなクエリを試してみたが、project の docID を入れないとダメっぽい。こういうことが出来るのが subCollection だと期待したんだけど、ダメっぽいな。
QuerySnapshot snapshot = await _firestore.collection("user").doc(userId).collection('project').doc().collection('task').where('status', isEqualTo: 0).get();
結局、どうするかというと。user のドキュメント以下の project と同じ階層に task の subCollection を持つのは変わらない。ただし project のドキュメントに task の情報を持たせるんじゃなくて、task の方に親の project の情報を持たせることにした。
なぜ最初から、そうしなかったかというと、これをやると必ず project と task の二回クエリを発行しなければならなくなる。できれば、project に紐づく task を RDB の join のような形で一発で取得したいという気持ちがあったからだ。
まだまだ RDB 脳から抜け切れてないらしい。
新しい構造としては以下のようになる。
user: [
{
id: "abcd0123",
name: "taro",
created_at: "2020-09-09 00:00:00",
project: [
id: "aaa000",
title: "ProjectA",
status: 0,
created_at: "2020-09-09 00:00:00",
updated_at: "2020-09-09 00:00:00",
],
task: [
{
id: "tasktask",
title: "がんばる",
is_done: False,
project_id: "aaa000",
created_at: "2020-09-09 00:00:00",
updated_at: "2020-09-09 00:00:00",
}
]
}
]
これで、なんとかなるはず。
最後に
やっぱり NoSQL の設計は難しい。実際にクエリ書いてみないと分からないことが多い(これは経験が無いからだと思うけど)。これくらいの設計なら、何も考えずにサクサク設計できるように、ならなくては!っと思うけど、実際どうなんだろう?
今回の FireStore は subCollection とか様々な機能が着いてるし、インデックスも全てのカラムに適応してくれるらしいので、かなり簡単なほうだと思うけど、DynamoDB とか使うなら別のノウハウが必要になりそう。
次は、実際に Firestore 使って project や task を登録する部分を作る予定。