firestore arrayUnionを使ってupdateする
firestoreにarrayの配下にmapがあるようなフィールドをupdateするときに、 プロジェクトのメンバーが複雑な分岐処理を書いていたので、 arrayUnionで簡単になるんじゃない?と思い調べました。
最初メンバーが書いていたコードはこんな感じです。 firestore_refを使ってるので、本当はちょっと違いますが。 あと、テストしてないので、動くかわからないです。
updateFcmTokens(device, token) async { final newFcmToken = { "device": device, "token": token }; // まずはusersの特定のドキュメントを取得 final userDoc = FirebaseFirestore.instance .collection('users') .doc('uid'); final snap = await userDoc.get(); // 本当はstreamで取りたい final user = snap.data(); if (user == null) return; // これは例外的なケース final fcmTokens = user['fcmTokens'] as List<Map>; if (user['fcmTokens'] == null) { userDoc.update({ 'fcmTokens': [newFcmToken] }); } else { // 同じものがないか調べる final isUpdate = fcmTokens .where((fcmToken) => fcmToken['token'] == token) .isEmpty; if (isUpdate) { // 同じものがない場合は更新 // ここのaddも冗長 fcmTokens.add({ 'token': token, 'device': device}); userDoc.update({'fcmTokens': fcmTokens}); } } }
こういう時はまず、自分の場合はadminSDKで同じことができるならそっちで検証します。 ちょっと長いので、こちらに置いてあります。
firebase-practice/array_map_update.ts at main · na8esin/firebase-practice · GitHub
ちょっと前に書いたarrayRemoveの検証に似てます。
adminSDKで試してみて、使い方がわかったので、flutterのソースをこんな感じで直しました。
// tokenはログインのタイミングなどでFirebaseMessaging.getToken()した値 updateFcmTokens(device, token) async { final fcmToken = {"device": device, "token": token}; userDoc.update({"fcmTokens": FieldValue.arrayUnion([fcmToken])}); }
単純に書き直すならこれだけでいいと思います。 同じものがある時は、何も追加されずに更新もされないので。 リクエストする回数も最大で1回(get)ありましたが、こちらも1回です。
ただ、ログイン時に必ず、最近保存したfcmTokenをusersなんかと一緒に、firestoreから 取得しているような場合は、FirebaseMessaging.getToken()したときに、 同じものかどうか比較して、余計なupdateを防いでもいいかもしれません。