I have heard of a new MapDiff
feature in Cloud Firestore Security rules.
I want to use this to more efficiently solve the problem described here, i.e. avoid having to write out the following:
allow update: if request.resource.data.size() == 6
&& request.resource.data.likes == resource.data.likes
&& request.resource.data.name == resource.data.name
&& request.resource.data.date == resource.data.date
&& request.resource.data.body == resource.data.body
&& request.resource.data.title == resource.data.title
&& request.resource.data.cakes is int;
In the past, writeFields
existed, but they are unsupported now.
How can I effeciently (concerning code conciseness) check if only a single field has been altered and use MapDiff
in general?
allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(['cakes'])
&& request.resource.data.cakes is int;
MapDiff
explanationrules.MapDiff
is a powerful feature recently added to Cloud Firestore Security Rules and is an efficient way of comparing two Map
objects.
Since request.resource.data
and resource.data
are maps, MapDiff
is perfect for this.
Map.diff()
In order to use any of the MapDiff
functionality, you will first have to call diff
on your map. This is very easy and looks like this:
request.resource.data.diff(resource.data) // Now you have a MapDiff object!
affectedKeys
The MapDiff.affectedKeys
function is the most useful because it combines addedKeys
, removedKeys
, and changedKeys
. This means that affectedKeys
is what will solve the problem from the original question.
This means that all of:
allow update: if request.resource.data.size() == 6
&& request.resource.data.likes == resource.data.likes
&& request.resource.data.name == resource.data.name
&& request.resource.data.date == resource.data.date
&& request.resource.data.body == resource.data.body
&& request.resource.data.title == resource.data.title
&& request.resource.data.cakes is int;
Turns into just:
allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(['cakes'])
&& request.resource.data.cakes is int;
So affectedKeys
returns a rules.Set
of all keys that are affected between the two maps, i.e. added, removed, or changed.
addedKeys
This works the same way affectedKeys
does, now only returning a set with added keys.
{'cakes': 1}.diff({}).addedKeys() == ['cakes'].toSet() // true
removedKeys
MapDiff.removedKeys
is the opposite of MapDiff.addedKeys
and returns a set of only the keys removed between the two maps:
{}.diff({'cakes': 1}).removedKeys() == ['cakes'].toSet() // true
changedKeys
MapDiff.changedKeys
returns a Set
of all keys that were changed between the two maps:
{'cakes': 0}.diff({'cakes': 1, 'pies': 4}).changedKeys() == ['cakes'].toSet() //true
A change is whenever the values in both sets are not equal.
unchangedKeys
MapDiff.unchangedKeys
is the opposite of MapDiff.changedKeys
and returns a set of all keys that were not changed between the two maps, i.e. all keys with equal values:
{'cakes': 0}.diff({'cakes': 0}).unchangedKeys() == ['cakes'].toSet() // true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With