フクシャチョーです。
今日はCloudFunctionsでフォロワー・フォローのカウントの集計をします。
よくある、あの数字の表示をするものです
アプリで、Aさんのフォローボタンを押すと、
自分のFollowingのカウントと、AさんのFollowerカウントが+1になります。
逆に、Aさんのフォロー解除ボタンを押すと、
自分のFollowingのカウントと、AさんののFollowerカウントが−1になります。
これをCloudFunctionsで集計します。
アプリ側の実装
データベースはCloudFirestoreを使います。
アプリからフォロー、フォロー解除のアクションを起こした時に、
relationshipsコレクションにドキュメントを追加、削除するようにします。
ドキュメントのIDは「フォローする側のID_フォローされる側のID(followerId_followedId)」とします。
CloudFunctions側の実装
CloudFunctionsでは、relationshipsコレクションに、ドキュメントが追加、削除されたイベントで、userコレクションの、対象ユーザーのドキュメントのfollowerCountとfollowingCountを更新します。
イベントタイプ
今回はrelationshipsコレクションに、ドキュメントが追加、削除されたのをトリガーとしたいので、onWriteを実装します
Cloud Firestoreのイベントタイプとトリガーについて
Cloud Firestore は、create
、update
、delete
、write
イベントをサポートしています。
イベントタイプ | トリガー |
---|---|
onCreate | ドキュメントが最初に書き込まれたときにトリガーされます。 |
onUpdate | すでに存在するドキュメントの値が変更されたときにトリガーされます。 |
onDelete | データを含むドキュメントが削除されたときにトリガーされます。 |
onWrite | onCreate 、onUpdate または onDelete がトリガーされたときにトリガーされます。 |
イベントデータ
さて、ここからイベントに含まれるデータを取得するのですが、今回利用しているSDKはv1.0のAPIとなります。
v1.0からイベントデータが DataSnapshot
に変更されています。
以前のリリースでは、event.data
は DeltaSnapshot
でしたが、現在の v 1.0 では DataSnapshot
です。
onWrite
イベントと onUpdate
イベントのデータ パラメータには before
フィールドと after
フィールドがあります。これらはそれぞれ DataSnapshot
です。
変更前後のデータを取得するのは以下のように記述します
exports.dbWrite = functions.firestore.document('/path').onWrite((change, context) => { const beforeData = change.before.data(); // data before the write const afterData = change.after.data(); // data after the write });
さて、実際にrelationshipsコレクションのCloudFunctionsを実装してみましょう。
引数で受け取るchangeは以下のようなデータが入って来ます。
Change { before: QueryDocumentSnapshot { _ref: DocumentReference { _firestore: [Object], _referencePath: [Object] }, _fieldsProto: { createdAt: [Object], followedId: [Object], followerId: [Object] }, _readTime: undefined, _createTime: '2018-05-13T06:56:27.184847000Z', _updateTime: '2018-05-13T06:56:29.831391000Z' }, after: DocumentSnapshot { _ref: DocumentReference { _firestore: [Object], _referencePath: [Object] }, _fieldsProto: undefined, _readTime: undefined, _createTime: undefined, _updateTime: undefined } }
beforeにデータがあり、afterにデータがない場合は削除
beforeにデータがなく、afterにデータがある場合は登録
となります。
その為、CloudFunctions内では、変更前後のデータがない場合は無効とし、
変更前のデータが存在する場合は、カウントを-1
変更後のデータが存在する場合は、カウントを+1としています。
export const updateFollowerCounts = functions.firestore .document("relationships/{relationshipId}") .onWrite((change, context) => { if (change.after.data() && change.before.data()) { return null; } let countChange; if( change.before.data()){ countChange = -1 } if( change.after.data()){ countChange = 1 }
次にcontextのパラメータから、relationshipIdを取得します。
contextには以下のようなデータが入って来ます
{ eventId: '070b6d17-f88c-4d5a-babe-0b93db6d95a9-0', timestamp: '2018-05-13T06:56:29.831391Z', eventType: 'google.firestore.document.write', resource: { service: 'firestore.googleapis.com', name: 'projects/fir-dev-15eca/databases/(default)/documents/relationships/AAsPEJFotxeEI4nnXud5g7n2OOF3_wnufjmOvkdctnfQQy5ckx2EKSrr2' }, params: { relationshipId: 'AAsPEJFotxeEI4nnXud5g7n2OOF3_wnufjmOvkdctnfQQy5ckx2EKSrr2' } }
relationshipIdは「フォローする側のID_フォローされる側のID(followerId_followedId)」となっているのでsplitで分割します。
// Get the user ids const ids = context.params.relationshipId.split("_"); const followerId = ids[0]; const followedId = ids[1];
CloudFirestoreのトランザクション処理
さてここからがCloudFirestoreのトランザクション処理に入ります。
フォローする側のユーザードキュメントとフォローされる側のユーザードキュメントを同時に更新する必要があるためです。
// Reference the document locations const db = admin.firestore(); const followerRef = db.collection("users").doc(followerId); const followedRef = db.collection("users").doc(followedId); // Return a transaction promise return db.runTransaction(async t => { // Fetch the data from the DB const follower = await t.get(followerRef); const followed = await t.get(followedRef); // format the counts const followerUpdate = { followingCount: (follower.data().followingCount || 0) + countChange }; const followedUpdate = { followerCount: (followed.data().followerCount || 0) + countChange }; // run the updates await t.set(followerRef, followerUpdate, { merge: true }); await t.set(followedRef, followedUpdate, { merge: true }); return t; });
アプリ側で、フォローしたり、アンフォローしてみましょう。
対象ユーザーのfollowingCountとfollowerCountが期待通りに増減すればOKです。
これで、アプリ側のユーザー画面では、userコレクションのデータを取得するだけで、以下のような数字を表示することができますね。
Firebase SDK for Cloud Functions 移行ガイド: ベータ版からバージョン 1.0 へ
Cloud Firestore トリガー | Firebase