Cloud Functionsでview数カウンターを作ろう、GA使わんよ


はじめまして。
マインドツリーを使ってメモ書きするサービスQ&Qを作っているあどにゃーです。
突然ですがQiitaの記事のオーナーだけが見れることview数って良くないですか?

記事のオーナーしか見れないのでマウントの取り合いにもならないですし、記事のオーナはモチベーションにも繋がります。
ということで、Q&Qにもこの機能を実装してみたいと思います。

今回はCloud Functionsを使って、記事の閲覧数を記事のオーナがチェックできるようにする機能の解説になります。Google Analytics(GA)つかったサイト全体の解析とかではないです

個人開発のつらみ

個人開発サービスは基本過疎なので一つの記事がどれくらいの人に届いているか不明でモチベーションが難しくなることが多いです GA解析も全体俯瞰はできますが、一つひとつの記事がどう見られているかってのは案外わかりにくいです 個人開発にこそ、記事のオーナがView数を見れる機能は付与してみるといいんじゃないかなと思います!

全体実装の流れ

  1. ユーザが記事を閲覧したらUserのuidをFirestoreに保存します。
  2. 記事のFirestore上にuidが作成されたことをトリガーにCloud Functionsを発火させます。
  3. view数をインクリメントしてview数を管理するFirestore上に書き込みます。

1. uidをFirestoreに保存

UserのuidはfirebaseのAuthenticationの機能でかんたんに取得することができます。

const uid = firebase.auth().currentUser.uid

これをuserが記事(tree)を開いてreadしたタイミングで、trees/treeId/viewers/{uid}に書き込みます。

記事のuidsをそのまま読み込むのはだめなの?

trees/treeId/viewers/{uid}にuserのuid Dataがあるなら、下記のようにそのまま読めばview数がわかるのですが、このやり方はイケていません

db.collection('trees'+treeId+'viewers').get().then(snap => {
   size = snap.size // viewersのコレクションのサイズを返す
})

イケてない理由は2つです。
1. コレクションを全部GETするやり方はuid(view数)が100を超えたあたりで重くなり処理に時間がかかる
2. 記事のオーナが権限上全員のuidをgetできてしまうのはセキュリティルール上弱い

ということで、Admin権限のCloud Functionだけがuidのコレクションをreadできるようにして、記事のオーナはCloud Function経由でview数をインクリメントした先だけ見れるようにします。

2. 記事上にuidが作成されたことを契機にCloud Functionsを発火

こちらはスタンダードな使い方で、trees/{treeId}/viewers/{uid}が作成されたことをトリガーにするonCreateでイベントを発火させます。

 export default functions.firestore
   .document('trees/{treeId}/viewers/{uid}')
    .onCreate( async (snap: any, context: any) => {
})

3. view数をインクリメントしてview数を管理するFirestore上に書き込み

発火させたFunctionsの中で、view数をインクリメントします。インクリメントなので、admin.firestore.FieldValue.increment(1)するだけです。

 export default functions.firestore
   .document('trees/{treeId}/viewers/{uid}')
    .onCreate( async (snap: any, context: any) => {
      const treeView = {
        views: admin.firestore.FieldValue.increment(1)
      }
      // tree ownerのuidを取得
      const treeDoc = await db.collection('trees').doc(context.params.treeId).get()
      const treeData: any = treeDoc.data() ? treeDoc.data() : { userId: false }
      const ownerUid = treeData.userId
      // Treeのview数の更新
      try {
        await db.collection('views/' + ownerUid + '/trees').doc(context.params.treeId).update(treeView)
      } catch (err) {
        throw err
      }
})

まとめ

マインドツリーを使ってメモ書きするサービスQ&Qにも、記事オーナがview数をチェックする機能を実装できました。
めでたしめでたし