22

In Cloud Firestore Rules - I have a document called task and I want to see if some data (assignee field) is null / don't exists.

I've tried:

  1. resource.data.assignee == null - Does not work (Error)
  2. !resource.data.hasAll(['assignee']) - Does not work (Error)

From the documentation - it states that this indeed creates an error: // Error, key doesn't exist allow read: if resource.data.nonExistentKey == 'value';

Dan McGrath
  • 41,220
  • 11
  • 99
  • 130
Gal Bracha
  • 19,004
  • 11
  • 72
  • 86
  • Here is the full rules file - https://github.com/Metaburn/doocrate/blob/master/firestore.rules As I understand `resource` is a document since I'm using `match /tasks/{anyTask} {` From firebase documentation on resource - https://firebase.google.com/docs/firestore/reference/security/#resource_1 – Gal Bracha Oct 08 '17 at 12:05
  • I don't need to check for a document - I know the document is there. I want to check for a field inside that document. so for `/tasks/my-task` - `tasks` is the collection. `my-task` is the document. And inside there is a field `assignee` - I want to see if it is null or not and I can't – Gal Bracha Oct 08 '17 at 12:11
  • There is a `resource.data.keys()` function, try using `hasAll(['assignee'])` on this instead. – Callam Oct 08 '17 at 12:19
  • @Callam Look at the Question at number 2 - As I've wrote - !resource.data.hasAll(['assignee']) - Does not work (Creates an Error) – Gal Bracha Oct 08 '17 at 12:20
  • 2
    Maybe you'll have some luck with `!resource.data.keys().hasAll(['assignee'])` – Callam Oct 08 '17 at 12:21
  • I'll make it an answer, glad we got there! – Callam Oct 08 '17 at 12:26
  • 1
    Boom @Callam You got it. – Gal Bracha Oct 08 '17 at 12:27
  • Yes, you missed `keys()` – Vlad Feb 21 '18 at 11:57

3 Answers3

34

Reading the list comparisons of the Firestore Security rules documentation here, we can see that hasAll returns true if all values are present in the list.

// Allow read if one list has all items in the other list
allow read: if ['username', 'age'].hasAll(['username', 'age']);

The request.resource.data is a map containing the fields and values. In order to use hasAll, we must first get the keys as a list of values as shown here.

!resource.data.keys().hasAll(['assignee'])
Callam
  • 11,409
  • 2
  • 34
  • 32
  • 2
    Should `!('asignee' in resource.data.keys())` work too? Another thing to note is this, from the rules docs: "Fields not provided in the request which exist in the resource are added to `request.resource.data`. Rules can test whether a field is modified by comparing `request.resource.data.foo` to `resource.data.foo` knowing that every field in the `resource` will also be present in `request.resource` even if it was not submitted in the write request.", so there may be times when the key exists and you aren't expecting it to... – Andrew M. Jan 10 '18 at 17:10
  • @menehune23 That's true, and it makes it really difficult to tell if the client app has _not_ included a field, in the case where it might already exist in the resource. In addition, the copying of resource to request fields is _not_ honoured by the rules simulator, so you can build an update in the simulator which works, but _won't_ work in a real app... Very frustrating. – dsl101 Oct 08 '18 at 21:09
14

Looking at the docs - https://firebase.google.com/docs/reference/rules/rules.Map

k in x  - Check if key k exists in map x

so this should work (without the keys())

!('assignee' in resource.data) 
akauppi
  • 17,018
  • 15
  • 95
  • 120
pdkn
  • 141
  • 1
  • 2
5

if you want to make sure a key is null you need to check that this key is not part of the resource keys property:
!resource.data.keys().hasAny(['assignee'])

you can also use hasAll or hasOnly. more info here

Sagiv Ofek
  • 25,190
  • 8
  • 60
  • 55