Cloud Firestoreのセキュリティルールにかつて存在した`writeFields`を覚えていますか?

3 min読了の目安(約3000字TECH技術記事

覚えてますか?...僕は最近までその存在をすっかり忘れていました。
かれはいつ消えたのか、代わりとなる機能MapDiffについてお話します。

かつて存在したrequest.writeFields

Cloud Firestoreのセキュリティルールには、かつてrequest.writeFieldsという変数がありました。
主にcreateupdateのオペレーションのルールを書くときに、変更を加えようとしているフィールドのオブジェクトを参照することが可能でした。

例えば以下のドキュメントが存在し、

{
  name: 'Taro',
  age: 29,
  country: 'Japan'
}

以下のデータを書き込みupdateを行う場合、

{
  nickName: 'tarotaro',
  age: 30
}

request.writeFieldsを参照すると以下のオブジェクトが参照できました。

{
  nickName: 'tarotaro',
  age: 30
}

resource.dataでは現在のドキュメントフィールドの状態を、request.resource.dataでは書き込みが成功した場合の将来のドキュメントフィールドの状態を取得するため、「何が書き込まれ更新されるのか」という差分を取得するにはこのrequest.writeFieldsがとても便利でした。

しかし...

しかし、これは2018年の10月ごろからDeprecatedになり、公式のドキュメントからもリファレンスが削除されました。
しばらくはそのまま使えていたのですが、(僕も正確なタイミングは知らないのですが)おそらく2020年の3月頃にはRemovedされ完全に消えてしまったようです。

Stack Overflowのこちらの回答によると、

  • Oct 4, 2018: deprecated
  • Mar 4, 2020: Map.diff() replaces writeFields functionality

と記述があります。

代わりに登場したMapDiff

writeFieldsが消える少し前、2020年2月ごろに、MapDiffという型と、2つのMap型の変数からMapDiff型を生成するdiff()関数が登場しました。

このリリースに関してはリリースノートにてアナウンスがあり、その後Firebaseのブログでも紹介されていました。

新しく登場したMapDiffにより、それまで不可能だったオブジェクトの差分を取得することができるようになりました。

map1.diff(map2) // type of `MapDiff`

MapDiffの持つ5つの関数

MapDiffは5つの関数を持ちます。

  • addedKeys(): 2つのmapを比べて、追加されたフィールドのkeyのセットを返します
  • removedKeys(): 2つのmapを比べて、削除されたフィールドのkeyのセットを返します
  • changedKeys(): 2つのmapを比べて、変更されたフィールドのkeyのセットを返します
  • affectedKeys(): 2つのmapを比べて、影響が生じたフィールドのkeyのセットを返します。つまりaddedKeys() + removedKeys() + updatedKeys()を表します
  • unchangedKeys(): 2つのmapを比べて2つに共通して存在しているフィールドのうち、変更を受けなかったフィールドのkeyのセットを返します

書き込み前後での差分を取得する

request.resource.data.diff(resource.data)

これでwriteのオペレーションにて、書き込み前後で生じたdiffの情報が取得できます。
あとは必要に応じてどういう差分が生じたのか、そのkeyのセットを取得してセキュリティルールの構築に役立てます。

request.resource.data.diff(resource.data).addedKeys()
request.resource.data.diff(resource.data).removedKeys()
request.resource.data.diff(resource.data).changedKeys()
request.resource.data.diff(resource.data).affectedKeys()
request.resource.data.diff(resource.data).unchangedKeys()

関数化して扱いやすく

次のように関数化して使用すると記述量も減らせて楽に記述することが可能になります。

function dataDiff() {
  return incomingData().diff(existingData());
}

function incomingData() {
  return request.resource.data;
}
      
function existingData() {
  return resource.data;
}
allow create: if dataDiff().unchangedKeys() == [...].toSet();

参考

最後に

もし記事が役に立った!参考になった!という場合はLikeしてもらえると嬉しいです 🙌
今後の技術ネタの投稿のモチベーションになります。
サポートもお待ちしています 🐈