Cloud Firestoreのbatchを使って一括書き込み

公開日時
更新日時

Cloud Firestoreを使って、写真に対するいいね機能を実装した際に以下のようにデータ設計を行った。

  • users/${authUser.uid}/like_photos としてサブコレクションを作成し、自分がいいねした写真一覧を保持
  • photos/${targetId}/like_users としてサブコレクションを作成し、写真にいいねしたユーザ一覧を保持

いいね及びいいね削除の際に、上記2つのサブコレクションに書き込みを行う。

この書き込みはアトミックな処理として行いたいので、一括書き込みができるbatchを利用した。

実装サンプル

  • firestoreにデータ書き込みを行うDBインスタンスを作成

nuxtアプリの場合、プロジェクト独自のファイルはどこに置くのがいいんだろう…

今回はsrc以下にprojectディレクトリを作成し、その下に配置することにした。

// src/project/DB.ts
import firebase from '~/plugins/firebase'
import IDB from '~/types/IDB'
import IAuthUser from '~/types/IAuthUser'

const firestore = firebase.firestore()

class DB implements IDB {
  async like(targetId: string, authUser: IAuthUser) {
    const like = {
      targetId: targetId,
      fromUid: authUser.uid,
      createdAt: firebase.firestore.FieldValue.serverTimestamp()
    }
    const batch = firestore.batch()
    const userRef = firestore.doc(`users/${authUser.uid}/like_photos/${targetId}`)
    batch.set(userRef, like)
    const photoRef = firestore.doc(`photos/${targetId}/like_users/${authUser.uid}`)
    batch.set(photoRef, like)
    await batch.commit()
  }
  async unlike(targetId: string, authUser: IAuthUser) {
    const batch = firestore.batch()
    const userRef = firestore.doc(`users/${authUser.uid}/like_photos/${targetId}`)
    batch.delete(userRef)
    const photoRef = firestore.doc(`photos/${targetId}/like_users/${authUser.uid}`)
    batch.delete(photoRef)
    await batch.commit()
  }
}

const db = new DB()
export default db
  • 写真詳細ページ

呼び出し元となる写真詳細ページで、Firestoreへの書き込みとstoreの操作を行うようにした。

// src/pages/photos/_id.vue
import db from '~/project/DB'

export default class PhotosShow extends Vue {
  // ...

  async like() {
    this.loading()
    try {
      await db.like(this.photo.id, this.authUser)  // batch書き込み
      this.$store.dispatch('authUser/like', this.photo.id)  // store操作
      this.$store.dispatch('photo/like')
    } catch (error) {
      console.error(error)
    } finally {
      this.loaded()
    }
  }

  async unlike() {
    this.loading()
    try {
      await db.unlike(this.photo.id, this.authUser)
      this.$store.dispatch('authUser/unlike', this.photo.id)
      this.$store.dispatch('photo/unlike')
    } catch (error) {
      console.error(error)
    } finally {
      this.loaded()
    }
  }
}

参考


Related #firebase

YouTubeの「Firebase Release Notes」プレイリスト

最新のアップデートの概要を把握するのにちょうど良い

Nuxt.jsのservice workerで環境変数を切り替えられるようにする

設定情報をハードコードしたくなかったので、ビルド時にnodeコマンドでファイル生成を行い環境変数を切り替えられるように対応した。

CloudFunctionsを使ってFirestoreのサブコレクションを削除する

CloudFunctions内でFirebase CLIのdeleteコマンドを呼び出すことで一括削除ができる

Firebase Hostingのリリース履歴を一括削除する

いつの間にか一括削除機能が追加されていた