フクシャチョーです。
今日は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