NoSQLであるFirestoreでミニブログを作るときの構成を考えます。
Firestoreの組み方を勉強中ですので、自分の整理のためにも書いています。
ミニブログを考える
例として、ツイッターのようなつぶやきサービスについて考えます。
仕様
- つぶやきを投稿できる
- 他人をフォローできる
- フォロワーを確認できる
- 特定ユーザーのつぶやきだけを見られる
- フォローしているユーザーのつぶやきだけを見られる
覚えておかなければいけないルール
- 1つのドキュメントは最大1MB
- フィールドに値は20000個まで(配列/Mapの一要素も1つとカウント)
- ドキュメントへは1秒1回の書き込み制限がある
- 課金はRead/Writeのドキュメント数
1つのドキュメントにまとめる
ユーザーコレクションでユーザー1人につき1ドキュメント与えられ、
ドキュメントはツイートの内容を配列として持ちます。
User user001{ id:user001, tweets:[tweet1, tweet2, tweet3], followee:[user002, user003, user004], follower:[user007, user008, user009] }
問題点
以下の問題が挙げられます。
1MB制限/20000制限
ツイート数が増えていくと、いずれ容量/個数制限に引っかかってしまいます。
他にも問題点はありますが、後述します。
ツイートをサブコレクションにする
tweetsサブコレクションを用意し、1つのツイートを1つのドキュメントとして作成します。
サブコレクションであれば、Userドキュメントの1MB制限/20000制限に引っかからないのでうまくいきそうですね。
User user001{ id:user001, tweets tweet1{ tweetid:001, tweet_text:"ついーーと", } tweet2{ tweetid:001, tweet_text:"ついーーと2", } tweet3{ tweetid:001, tweet_text:"ついーーと3", } followee:[user002, user003, user004], follower:[user007, user008, user009] }
問題点
例えば全ユーザーから全てのツイートを取得しようと大変...でした。少し前まで。
今まではコレクション間を超えてQueryを出すことはできず、
db.collection("User").document("user001").collection("tweets")
のようにQueryを出す必要があったので、
全ツイートを取得するにはユーザーの数分のQueryが必要でした。
ですが最近のアップデートで、 同じコレクション名であれば横断的にQueryを出すことができるようになりました。
つまり、いけます。
もちろんよくある手法の通りにツイートコレクションをRoot直下に置くのもいいですね。
その際には所有者もフィードで示す必要があります。
User user001{ id:user001, followee:[user002, user003, user004], follower:[user007, user008, user009] } user002{ id:user002, followee:[user001, user003, user004], follower:[user001, user008, user009] } Tweets tweet1{ tweetid:001, author:user001, tweet_text:"ついーーと" } tweet2{ tweetid:002, author:user002, tweet_text:"ついーーと2" } tweet3{ tweetid:003, author:user003, tweet_text:"ついーーと3" }
見たところいい感じですが、問題点はまだあります。
followeeとfollowerをリストで持っていますと、
以下のような問題点が考えられます。
1MB制限/20000制限
フォロー / フォロワーに制限が出てしまいます。
2万フォロワーなんて世の中ザラにいます。私は20人にも満たないですが。
同期問題
例えばuser001がuser002のフォローを外したとします。
するとuser001ドキュメントのfolloweeからuser002を外すだけではなく、
user002ドキュメントのfollowerからuser001を外すこともしないといけません。面倒ですね。
User/ツイート/フォロー/フォロワーのコレクションをすべてRootに置く
全てをRootに置きました。
これなら1MB/20000制限にも掛かりません。
また、フォローフォロワーの更新は1つのドキュメント変更で済みます。
フォロー数の取得は
where(u'followee', u'==', u'user001')
でまとめて所得できます。
いい感じですね。
User user001{ id:user001, followee:[user002, user003, user004], follower:[user007, user008, user009] } user002{ id:user002, followee:[user001, user003, user004], follower:[user001, user008, user009] } Tweets tweet1{ tweetid:001, author:user001, tweet_text:"ついーーと" } tweet2{ tweetid:002, author:user002, tweet_text:"ついーーと2" } tweet3{ tweetid:003, author:user003, tweet_text:"ついーーと3" } Follow follow1{ followee:user001, follower:user002 } follow2{ followee:user001, follower:user002 } follow3{ followee:user001, follower:user002 }
NoSQLで冗長性を持たせることは別に変なことではないので、
RootにTweetsコレクションを用意し、Userドキュメントにも同じTweetsサブコレクションを置く考え方も間違っていないと思います。
参考させていただいたサイト
待ち焦がれたCollectionGroupがCloud Firestoreへやってきた。 - Qiita
https://www.youtube.com/watch?v=haMOUb3KVSo&list=PLl-K7zZEsYLluG5MCVEzXAQ7ACZBCuZgZ&index=5