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 を出すことはできないため、全ツイートを取得するには下記のような Query を出す必要がありました。
db.collection("User").document("user001").collection("tweets")
ですが最近のアップデートで、同じコレクション名であれば横断的に 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 万フォロワーなんて世の中たくさんいます。
同期問題
例えば 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
How to Structure Your Data | Get to know Cloud Firestore #5 - YouTube
【Firebase】Cloud Firestore のデータ構造の決め方を Firebase の動画から学ぶ - Qiita