I want to create an increment field for article likes.
I am referring to this link: https://firebase.google.com/docs/database/android/save-data#save_data_as_transact
The security rules can do a few things:
ensure that a user can only add/remove their own uid to the stars node
"stars": {
"$uid": {
".write": "$uid == auth.uid"
}
}
ensure that a user can only change the starCount when they are adding their own uid to the stars node or removing it from there
starCount by 1Even with these, it might indeed still be tricky to have a security rule that ensures that the starCount is equal to the number of uids in the stars node. I encourage you to try it though, and share your result.
The way I've seen most developers deal with this though is:
stars node is not too large, this is reasonable).stars into starCount. It could use child_added/child_removed events for incrementing/decrementing.I wrote up a working example of a voting system. The data structure is:
votes: {
uid1: true,
uid2: true,
},
voteCount: 2
When a user votes, the app sends a multi-location update:
{
"/votes/uid3": true,
"voteCount": 3
}
And then to remove their vote:
{
"/votes/uid3": null,
"voteCount": 2
}
This means the app needs to explicitly read the current value for voteCount, with:
function vote(auth) {
ref.child('voteCount').once('value', function(voteCount) {
var updates = {};
updates['votes/'+auth.uid] = true;
updates.voteCount = voteCount.val() + 1;
ref.update(updates);
});
}
It's essentially a multi-location transaction, but then built in app code and security rules instead of the Firebase SDK and server itself.
The security rules do a few things:
Note that the rules don't:
.write rule)The rules:
"votes": {
"$uid": {
".write": "auth.uid == $uid",
".validate": "(!data.exists() && newData.val() == true &&
newData.parent().parent().child('voteCount').val() == data.parent().parent().child('voteCount').val() + 1
)"
}
},
"voteCount": {
".validate": "(newData.val() == data.val() + 1 &&
newData.parent().child('votes').child(auth.uid).val() == true &&
!data.parent().child('votes').child(auth.uid).exists()
) ||
(newData.val() == data.val() - 1 &&
!newData.parent().child('votes').child(auth.uid).exists() &&
data.parent().child('votes').child(auth.uid).val() == true
)",
".write": "auth != null"
}
jsbin with some code to test this: http://jsbin.com/yaxexe/edit?js,console