I am doing a batch write like so:
const batch = this.afs.firestore.batch();
const studentID = this.afs.createId();
const classID = this.afs.createId();
const studentRef = this.afs.doc(`students/${studentID}`).ref;
batch.set(studentRef, {
name: 'tom',
classes: firebase.firestore.FieldValue.arrayUnion(classID)
});
const classRef = this.afs.doc(`classes/${classID}`).ref;
batch.set(classRef, {
name: 'calculus',
students: firebase.firestore.FieldValue.arrayUnion(studentID)
});
await batch.commit();
And I want to ensure that either:
students/studentID/classes
array can only be changed ifclasses/classID/students
array also changed with the correct IDs- this is not enforced if that field is not updated
- one field cannot be deleted unless the other field is deleted
So, I am thinking this:
match /students/{studentID} {
allow read;
allow write: if noChange('classes') ||
matchWrite(studentID, 'classes', 'students');
}
match /classes/{classID} {
allow read;
allow write: if noChange('students') ||
matchWrite(classID, 'students', 'classes');
}
function noChange(field) {
return !(field in request.writeFields);
}
function getVal(field) {
return resource.data[field].removeAll(request.resource.data[field])[0];
}
function matchWrite(VAL1, VAL2, VAL3) {
return VAL1 in
getAfter(/databases/$(database)/documents/$(VAL2)/$(getVal(VAL2))).data[VAL3];
}
delete - delete for references...
// allow delete: if noChange('classes') || matchDelete('classes', 'students');
// allow delete: if noChange('students') || matchDelete('students', 'classes');
// function matchDelete(VAL1, VAL2) {
// students/STUDENTID/classes array CLASSID (being removed)
// must eq classes/CLASSID/students array STUDENTID (being removed)
// and the other way around
// }
I am getting boggled down on the last part. Since the batches are atomic, I would think I can use getAfter()
somehow.
How would I ensure atomic rules or nothing?
J