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!
affectedKeysThe 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.
addedKeysThis works the same way affectedKeys does, now only returning a set with added keys.
{'cakes': 1}.diff({}).addedKeys() == ['cakes'].toSet() // true
removedKeysMapDiff.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
changedKeysMapDiff.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.
unchangedKeysMapDiff.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