Less obvious Firebase Cloud Firestore techniques
I have been using the recently GA (generally available) Cloud Firestore via REST API from Unity. I want to collect something that is not at the forefront or in documentation, that I think if I had known this it would change my database design and planning early.
Field Transforms
Things like “request server timestamp”, “array append”, and “array remove” are field transforms. So you do not either creating a new document or writing something new to the document. You ask them to change.
You don’t need to know the content in them but it will change according to their own content, that’s what make it called “transform”.
Write | Firebase
DynamicLinkItunesConnectAnalyticsParametersfirebase.google.com
- On any of your timestamp field if you set “request server timestamp” sentinel value, it will be replaced with server time on write. Easily understandable.
- Array append ask any array field to be added some elements. The catch is that duplicate element will not be added (but Firestore array could contains duplicate elements). This is great for function like add friends. You don’t need to do something like get all your friends -> add one new friends -> overwrite all of them back. Just use this field transform.
- Array remove is the same but remove what’s there. If you remove something that is not in the array, nothing happen!
- You notice increment maximum and minimum too. It could come in handy to your design. maximum ‘s use is for example to update highscore. You can write the score regardless if it is lower or higher than server value and field transform should take care of that.
What else can I do in Firestore rules?
You have probably went through this already. You know about future value, existing data value, creating functions, and validating auth.
Writing conditions for Cloud Firestore Security Rules | Firebase
The primary building block of Cloud Firestore Security Rules is the condition. A condition is a boolean expression that…firebase.google.com
But wait, from this page data you use in the rule is a Map
Interface: Resource | Firebase
DynamicLinkItunesConnectAnalyticsParametersfirebase.google.com
What is a Map ? Other than the dot notation you used to access your fields there are more things to play with.
Interface: Map | Firebase
DynamicLinkItunesConnectAnalyticsParametersfirebase.google.com
Map
- keys is a List of keys.
- size . You could then check if size is something you are expecting.
- values would be a List of data you used the dot notation to access, but they are in List instead.
- x in y this operator can check existence of a key in your map. (cool!)
What can you do with a List ?
Interface: List | Firebase
DynamicLinkItunesConnectAnalyticsParametersfirebase.google.com
List
- hasAll hasAny hasOnly you can give it an another List to compare and returns a boolean. I think this could be very good in designing rules.
- join returns new List , then maybe you could use has__ again.
- size
- x in y also available in list, but checks for value’s existence instead of key.
hasAll fails when there is no list
On the data I am going to write I am writing [1,2,3] to the field named nums . I asked if the future value of nums should contains the old nums array at least (So if the old nums is currently [1,3] that write is valid), so I used hasAll .
However hasAll does not work when I don’ have anums field yet and intended to create new from this update operation.
To check for field’s existence
Incorrect
allow update : if addedAsRival in resource.data == false;
allow update : if "addedAsRival" in resource.data == false;
== false need a parentheses because otherwise it would apply to resource.data .
The Map in check is checking for string key, so the thing in front of in need a string quotation.
Correct
allow update : if ("addedAsRival" in resource.data) == false;
allow update : if !("addedAsRival" in resource.data);
This rule would allow you to add rival only once where there are no rivals before. This is the complete rule for example :
I am using a scheme where you add yourself to other player’s “added as rival” list. This rule ensure that you can only add yourself whether the target has this field before or not. (It looks hideous but I don’t know how I could make it better while retaining the same intent)
Getting a subset of document
Multiple times throughout learning material it said that the smallest unit you can request is a document. But in REST API it is possible for server to returns only fields you want from one document via DocumentMask .
Method: projects.databases.documents.get | Firebase
DynamicLinkItunesConnectAnalyticsParametersfirebase.google.com
I don’t know if this exists in other API or not. But it could suggest that eventually you will be able to do it. So, no need to fear “private fields leaking” if you keep them in the same document, just use mask in your request and the returned document will be as if those fields does not exist.
(I saw the official Firestore video suggesting doing sub-document containing private data, so that getting public data document will not returns private data as the query is shallow.)
Avoiding PATCH header in Android
One thing about Android is that they could not do PATCH header.
Patch Request Android Volley
Thanks for contributing an answer to Stack Overflow! Please be sure to answer the question. Provide details and share…stackoverflow.com
But all PATCH-based Rest API could be replaced with write , which uses POST header. Also with write you have a choice to do field transforms.
Method: projects.databases.documents.write | Firebase
DynamicLinkItunesConnectAnalyticsParametersfirebase.google.com
Map field is auto-indexed for only 1 level
Just in case you missed it, in here
Index types in Cloud Firestore | Firebase
A single-field index stores a sorted mapping of all the documents in a collection that contain a specific field. Each…firebase.google.com
For each map field, Cloud Firestore creates one ascending index and one descending index for each non-array and non-map subfield in the map.