I'm having a hard time figuring out how to validate a multi-location update where the updates depend on each other.
Consider the following structure:
"votes": {
"$post_id": {
"$answer_id": {
"$user_id": {
".write": "...",
".validate": "..."
}
}
}
}
"users": {
"$user_id": {
"votes": {
"up": {
".write": "...",
".validate": "..."
},
"down": {
".write": "...",
".validate": "..."
}
}
}
}
The users can vote on posts' answers with -1 / +1 (or remove their votes, so with null as well). So far so good, I can validate that no problem. My problem comes when I want to validate the user's up/down vote counter as well.
Example scenario: user A votes on an answer with +1, which would also increment user B's up
counter with 1. How can I validate the up
field so that it only gets incremented (or decremented) when there's an actual new vote for that.
Also there are scenarios like when a user has already voted +1 and then changes it directly to -1. I'm having a really hard time validating updates like this.
Should I just consider adding a server layer and do every single updates through the server? Or is my approach totally wrong here (or the data structure?). Adding a server layer would pretty much solve every validation issue, but also would add one more point of failure so I'm trying to avoid that.
Edit:
Update function
function vote(postID: string, answerID: string, author: string, oldVal: number, newVal: number): firebase.Promise<void> {
let voteValue: number = newVal == 0 ? null : newVal; // -1, 0, 1, could be changed to boolean
return this.ref.child(`users/${author}/votes`).once('value', count => {
let updates = {};
updates[`votes/${postID}/${answerID}/${this.authService.current.$key}`] = voteValue;
if (voteValue == 1) {
updates[`users/${author}/votes/up`] = ++count.val().up;
if (oldVal == -1) {
updates[`users/${author}/votes/down`] = --count.val().down;
}
}
if (voteValue == -1) {
updates[`users/${author}/votes/down`] = ++count.val().down;
if (oldVal == 1) {
updates[`users/${author}/votes/up`] = --count.val().up;
}
}
if (voteValue == null && oldVal == -1) {
updates[`users/${author}/votes/down`] = --count.val().down;
}
if (voteValue == null && oldVal == 1) {
updates[`users/${author}/votes/up`] = --count.val().up;
}
this.ref.update(updates);
});
}
When an answer's author's current votes are 0/0 and another user upvotes one of his answers it would create an update like:
"votes/-KM0CMCIQuBsGWQAjhRQ/-KM0CVmhK_7JQcxtdixl/fxpxj1Ky4yVpEeeB5PZviMjqNZv1": 1
"users/spAnCEKTTjX1GgdybQZIlXRI9IG2/votes/up": 1