2
votes

I have a document in Cloud Firestore.

The structure of the "events" field is as follows:

{
"events" : [ 
    "eventID-1": [
        "value1",
        "value2"    
        ],
    "eventID-2": [
        "value2",
        "value3"    
        ],
    "eventID-3": [
        "value3",
        "value4"    
        ]
    ]
}

enter image description here

Updating [swift]:

self.db.collection("collection").document(document).updateData([
  "events.eventID-1": FieldValue.arrayUnion([value2])
...

How do I make up Firebase Cloud Firestore Security Rules:

  1. value were unique. That is, when writing, check if the value does not exist in arrays eventID-1, eventID-2, eventID-3 ....

  2. Check (length) the value that comes for recording

I just figured out how to check the incoming data, that it's a map and that we only get one value.

match /databases/{database}/documents {
    match /collection/{documentID} {
      allow write: if request.resource.data is map
                   && request.writeFields.size() == 1
    }
}

UPD : It's been dealt with:

  1. Checking the string length in the query
allow write : if request.resource.data.events.values()[0].size() < 100
  1. Checking uniqueness in the list
allow write : if (request.resource.data.events.keys()[0] in resource.data.events.keys()) == false

But I have not figured out how to solve my problems (((( Help!!!

2
I don't understand what you mean by "Check (length) the value that comes for recording". Please edit the question to be more clear. It will be helpful to see specific examples of what is valid and invalid.Doug Stevenson
Could you edit the question to remove the parts of the question that are no longer problems for you? I'm having trouble figuring out what the issue really is here. You don't just append updates - it's easier to rewrite the question to say what you mean.Doug Stevenson

2 Answers

3
votes

Here is a try:

allow write: if request.writeFields.size() == 1
    && geteventarray_after().size() == geteventarray_before().size() + 1 // Making sure that the size increased so that is is unique
    && getnewelement().size() < 100     // Making sure the new element size is < 100
    && getnewelement() is string        // Other valdiations...

    function getsubstr(str, from, to) {
      return str[from:to];
    }
    function geteventarrayname() {
      return getsubstr(input, "events.".size(), request.writeFields[0].size());
    }
    function geteventarray(map) {
      return map.get(["events", geteventarrayname()], [])
    }
    function geteventarray_after() {
      return geteventarray(request.resource.data);
    }
    function geteventarray_before() {
      return geteventarray(resource.data);
    }
    function getnewelement() {
      return geteventarray_after()[geteventarray_after().size()-1];  // It is added at the end
    }

EDIT: this is untested but should be a good start

1
votes

In order to check if there are existing values you could use array-contains or something similar, here is the documentation where there are a few examples, and would look similar to this:

let docRef = db.collection("events");
let query = docRef.whereField("events", arrayContains: "newValue")
query.getDocuments() { (querySnapshot, err) in
    if let err = err {
        print("Error getting documents: \(err)")
    } else {
        if (querySnapshot!.isEmpty) {
           //do what have to if the value do not exist
        } else {
           //do what have to if the value exists
        }
    }
}

Of course this is only an example but should be a good starting point for what you need, also important to note that this would be on your app's side, not on the firebase rules.

For the Length measurement, since you only add 1 value at a time, you could the following on your Firebase Rule:

allow write: if request.resource.data is map
               && request.writeFields.size() == 1 
               && request.resource.data.map.values[0].length < 100 //whatever value you want

Hope this Helped you.