Firebase
Firestore
CloudFirestore
SecurityRules

もう間違えない! Cloud Firestore rulesのresource, request.resource, writeFields

Cloud Firestoreのrulsを書く上でややこしい

  • resource 変数
  • request.resource 変数
  • writeFields 変数

のそれぞれの違いを説明しようと思います。これを見ればもう間違えることはなくなるはず。

TL;DR

  • resource: 現在DBに格納されているドキュメント
  • request.resource: 書き込みを行った場合に期待される将来のドキュメント(の状態)
  • writeFields: 書き込みを行う場合に、書き込もうとしているフィールドのkeyの一覧

それぞれの違い

resource

ドキュメントによると、

resource 変数は要求されたドキュメントを参照し、resource.data はドキュメントに格納されているすべてのフィールドと値のマップです。

とあるので、現在のドキュメントに格納されているフィールドと値のmapを参照できます。
rulesのそれぞれのオペレーションでは以下の通り。

  • read(get, list): 現在のDBに格納されているドキュメント
  • create: undefined
  • update: 現在のDBに格納されているドキュメント
  • delete: 現在のDBに格納されているドキュメント

request.resource

ルールセットが保留中の書き込みを許可する場合は、request.resource 変数にはドキュメントの将来の状態が含められます。ドキュメント フィールドのサブセットのみを変更する update オペレーションの場合、request.resource 変数にはオペレーション後の保留中のドキュメントの状態が含まれます。

つまり、書き込みのオペレーション(crate, update, delete)を実行した後の(保留)ドキュメント状態がmapとして格納される。なので

  • read(get, list): undefined(writeオペレーション時のみ存在するため)
  • create: 新規で書き込むドキュメントのフィールドと値のmap
  • update: 値の追加更新削除をした後の 将来の状態の フィールドと値のmap。
  • delete: undefined

となる。 update の場合は、追加更新削除後の 将来の状態 を表すので、あるオペレーションで既存のデータ {A, B}{C} を書き加えて更新する場合、 request.resource.data には {A, B, C} が含まれる。
一方で

writeFields

List of fields being written in a write request.

とあり、意訳すると、「書き込みリクエスト時に書き込まれるであろうフィールドのリスト」となる。

  • read(get, list): undefined
  • create: 書き込むフィールドのkey (request.resource.data.keys() と一致)
  • update: 今回更新追加削除するフィールドのkey (request.resource.data.keys()必ずしも一致しない 。update時に変更しようとしているフィールドのkeyのみ)
  • delete: undefined

表にしてみる

実際にそれぞれのオペレーションで、現在のフィールド、書き込むフィールドが与えられた時に、resource, request.resourcet, writeFields の値がどう変化するか表にまとめます。

read(get, list)

現在のフィールド 書き込むフィールド resource request.resource writeFields
{
  name: 'mike',
  age: 25
}
undefined {
  name: 'mike',
  age: 25
}
undefined undefined

create

現在のフィールド 書き込むフィールド resource request.resource writeFields
undefined {
  name: 'mike',
  age: 25
}
undefined {
  name: 'mike',
  age: 25
}
['name', 'age']

update

現在のフィールド 書き込むフィールド resource request.resource writeFields
{
  name: 'mike',
  age: 25
}
{
  company: 'Foobar Inc.'
}
{
  name: 'mike',
  age: 25
}
{
  name: 'mike',
  age: 25,
  company: 'Foobar Inc.'
}
['company']

delete

現在のフィールド 書き込むフィールド resource request.resource writeFields
{
  name: 'mike',
  age: 25
}
undefined {
  name: 'mike',
  age: 25
}
undefined undefined

参考