I have this structure in Firestore
{
"fields": ...
"fields": ...
"arrayFields":[
<index 0>
"field A": ...
"field B": ...
"field C": ...
<index 1>
"field A": ...
"field B": ...
"field C": ...
<index 2>
"field A": ...
"field B": ...
"field C": ...
]
}
and it seems not working below
if request.resource.data.arrayFields['field A'].size() < 12;
I want to limit array of map fields size
Cloud Firestore Security Rules refer to arrays as a List
, so I'll use that terminology here.
The below rules make use of a custom function to describe the check we are performing:
function doesFieldOfIndexInListHaveSizeLessThan(field, index, list, size) {
return index + 1 <= list.size() // is index in list's bounds
&& field in list[index] // does the target element have the named field
&& list[index][field].size() < size; // does the value of the element's field
// have a size less than the given value
}
doesFieldOfIndexInListHaveSizeLessThan('field A', 0, request.resource.data.arrayFields, 10);
To perform this check on multiple indexes, you must code each one in individually as security rules can not be iterated.
doesFieldOfIndexInListHaveSizeLessThan('field A', 0, request.resource.data.arrayFields, 10)
&& doesFieldOfIndexInListHaveSizeLessThan('field A', 1, request.resource.data.arrayFields, 10)
&& doesFieldOfIndexInListHaveSizeLessThan('field A', 2, request.resource.data.arrayFields, 10)
&& // and so on...
You could place this in another function to make the condition (not the implementation) more readable. When developing custom functions, you need to remember that they can call other functions but cannot recurse and their total call stack depth is limited to 10. Rewriting the above rules as a function would give:
function doesFieldInListElementsHaveSizeLessThan(field, list, size, startingOffset, elementsToCheck) {
return (elementsToCheck > 0 && elementsToCheck <= 10) // elementsToCheck must be between 0 & 10, otherwise fail early
&& startingOffset >= 0 // if starting index is negative, fail early
&& doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset, list, size)
&& (elementsToCheck > 1 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 1, list, size)
&& (elementsToCheck > 2 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 2, list, size)
&& (elementsToCheck > 3 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 3, list, size)
&& (elementsToCheck > 4 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 4, list, size)
&& (elementsToCheck > 5 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 5, list, size)
&& (elementsToCheck > 6 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 6, list, size)
&& (elementsToCheck > 7 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 7, list, size)
&& (elementsToCheck > 8 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 8, list, size)
&& (elementsToCheck > 9 && doesFieldOfIndexInListHaveSizeLessThan(field, startingOffset + 9, list, size)
}
// checks first 10 elements to see if their 'field A' is less than 10 characters long
doesFieldInListElementsHaveSizeLessThan('field A', request.resource.data.arrayFields, 10, 0, 10)
// checks next 5 elements to see if their 'field A' is less than 10 characters long
&& doesFieldInListElementsHaveSizeLessThan('field A', request.resource.data.arrayFields, 10, 10, 5);
Note: If you expect your array to be quite large, use a Firestore Collection instead of an array in a document. This will greatly simplify your security rules.