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()
}
}
}