158
110

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Cloud FirestoreとFirebase Cloud Storageを使ってソーシャル機能を実装する方法

Last updated at Posted at 2019-05-17

Cloud FirestoreとFirebase Cloud Storageを使ってソーシャル機能を実装する方法

こんにちは、Stamp Incnoriです。
金曜日の20時くらいからこの記事を書き始めて若干の後悔を感じながら記事を書きました。書き終わったら飲みに行きます。

この記事ではCloud FirestoreとFirebase Cloud Storageを使ってソーシャル機能を実装する方法を紹介します。

どの機能においてもですが、実装方法は__要件と照らし合わせて考えることが重要__です。ソーシャル機能も要件によって実装方法は異なってきます。要件によって実装がどう異なるかについても説明しようと思いますので最後まで読んで頂けると幸いです。
また、ここでのソーシャル機能とは__フォロー、フォロワー__に限定して話を進めます。

ソーシャル機能

早速ですが、フォロー、フォロワーをCloud Firestoreではどのように表現するか見ていきましょう。
__Query__と__SubCollection__が利用可能なCloud Firestoreでは2種類の実装方法が考えられます。

Queryでソーシャル機能を実装
フォロワーを表す
/social/:social_id
// Document data
{
  "createdAt": Timestamp,
  "updatedAt": Timestamp,
  "from": {
    "id": "USER_ID",
    "name": "USER_NAME",
    "thumbnail": "STORAGE_REFERENCE_PATH"
  },
  "to": {
    "id": "USER_ID",
    "name": "USER_NAME",
    "thumbnail": "STORAGE_REFERENCE_PATH"
  }
}
SubCollectionでソーシャル機能を実装
フォロワーを表す
/social/:followee_id/followers/:follower_id

フォローしてる人を表す
/social/:follower_id/followings/:followee_id
// Document data
{
  "createdAt": Timestamp,
  "updatedAt": Timestamp,
  "name": "USER_NAME",
  "thumbnail": "STORAGE_REFERENCE_PATH"
}

2つの実装方法の違い

上記の二つの方法に大差はありません。 Read/Writeを考えた時に少しだけ差が出るので説明します。

2つの実装方法にWrite性能に大きな差が出ます。

WriteとReadを考える

Writeでは__SubCollection__の場合はWriteBatchを使って、2つのReferenceにDocumentを書き込む必要で__Query__の方は不要です。
問題になるのは、連続的なトラフィックが起こった場合です。Collectionへのインサートは秒間500回に制限されています。RootCollectionにSocialDocumentを配置した場合は性能が制限されるので__SubCollection__へ配置した方がいいでしょう。

Readでは__Query__を使う方にはwhereが必要で、__SubCollection__の方は不要です。
whereのレンジ表現に制限があるCloud Firestoreでは可能な限りwhereを使わずにかける__SubCollection__の方が若干優位かなとは思ってます。

以上のことから__SubCollection__の実装方法をオススメします。

DataのFieldについて考える

次にFieldについて考えていきましょう。今回はシンプルな__SubCollection__を用いた場合のFieldについて説明します。Fieldについても2種類の方法を紹介します。

JOINを使う実装
Field Description
createdAt データが作られた時間を記録
isAvailable 有効フラグ
isFollowing フォローフラグ
JOINを使わない実装
Field Description
createdAt データが作られた時間を記録
updatedAt データが更新された時間を記録
name ユーザー名
thumbnail ユーザーのサムネイル

2つの実装方法の違い

2つの実装では、Readが大きく異なります。どちらの方が適しているのかは要件によって変化すると思います。
InstagramとTwitterのフォロワーリストを比べて見るとInstagramでは表示されているデータが少ないのに対して、Twitterでは多くの情報が記載されているのがわかります。微妙な違いですが、データ構造やデータの取得方法には大きく影響します。

Instagram
スクリーンショット 2019-05-17 21.44.22.png

Twitter
スクリーンショット 2019-05-17 21.46.34.png

JOINを使う実装のメリット・デメリット

JOINを使う実装のメリットはなんと言っても新鮮なデータであること、デメリットはN+1のReadが発生すること😱

JOINを使わない実装のメリット・デメリット

メリットはReadが一回で終わること、デメリットは更新頻度が低いデータしか入れてはいけないことです。

両者とも優位性はある。

前者のN+1問題が起こる実装なんて考えられないと思うかも知れませんが、Cloud FirestoreのクライアントSDKではCacheが準備されています。
また、フォロワーリストの表示順が最新順であるとするならばデータはローカルにある可能性が高いと考えると通信はさほど発生しない可能性の方が高く、Readについて気にする必要はないかも知れません。例えばiOSのデフォルトでは100MBが割り当てられいるため、なかなかのデータがローカルに保存されています。またフォロワーを確認するタスクがメインとなるアプリはないはずなので、N+1 ≒ 100くらいと考えてしまってもいいかも知れません。

後者の実装を考えるときユーザーデータの更新頻度は低く、一般的なユーザーのフォロワー数は数百人であることが予想できます、全てのフォロワーのデータを更新したとしても大きな負荷ではありません。
しかし、もしユーザーデータが頻繁に更新される場合では(Nmax+1)x更新回数Writeが発生することになるので、どちらの実装にするのかは要件に合わせてください。

更新されないデータのみを入れておくのも手段の一つだと思います。

Firebase Cloud Storageを組み合わせる

FieldにあるthumbnailにはFirebase Cloud StorageのPathが入ることを想定しています。このPathには以下のようにフォーマットを固定するといいと思います。

/user/:user_id/thumbnail

個人的にはCloud Firestoreの構造とStorageの構造を一致させることは非常に効果的で、開発時に楽になりますし、セキュリティルールの記載も楽になります。またthumbnailなど特定の場所に使う場合のファイル名は固定するといいでしょう。こうすることで画像データの差し替えではDocument dataの更新は行わずに済みます。


Cloud FirestoreとCloud Storageをいい感じで連携できるライブラリ作りました。
iOSとTypeScriptで利用可能です。ぜひ試してみてください!!

Ballcap-1.png

https://github.com/1amageek/Ballcap-iOS
https://github.com/1amageek/ballcap.ts

それでは👍🏻

158
110
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
158
110

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?