0

Having following issue:

I denormalised my data structure in my Realtime Firebase Database. As a consequence of that I need to update the username in all other "posts" (in this case recipes) as well as in recipes with different authors. To do that, every time a user creates a comment on a recipe this user also creates a reference to this recipe in his user object.

Now when you change your username, it reads all those references and changes the username on this path like following (Swift):

ref.child("users/profile").child(uid ?? "ERROR").child("comments/DE").observeSingleEvent(of: .value, with: { (recipes) in
   // recipe dict is a dictionary of all the references to recipes a user has commented
   let recipeDict = recipes.value as! [String:Any]
   // now every reference is being used to change the username at given place to the new username with variable "username"
   var updateObjects: [String:String] = [:]
   for i in recipeDict.keys {
      updateObjects["recipes/DE/\(i)/comments/\(uid ?? "ERROR")/username"] = username
   }
   // Now the changes stored in the "updateObjects" variable will be applied
   ref.updateChildValues(updateObjects)
})

Now this works, but the problem with this approach is, that say a recipe gets deleted, the reference doesn't get deleted (would be complicated since security rules don't allow to update another users profile so therefore not their references as well). So that's not a huge deal for me, but since the 'updateChildValues' function creates the missing objects it is. What ends up happening is, that at the place where the deleted recipe would be, a "new" empty recipe structure without any other nodes than the comment gets created. This crashes my app and is not what I want. How can I stop this from happening?

Can you call a Multi-path update without writing data, when there is no existing structure (recipe was deleted)? Or do I need to remove all comment references and somehow give other users access to the comment references (which is tedious...)?

Bot Raid
  • 25
  • 5

1 Answers1

1

You could write security rules to reject a write operation to a non-existing comment if the write is just a username. For example, say that another property for each comment is the text, you could do:

"comments": {
  "$commentid": {
    "$uid": {
      "username": {
        ".write": "data.parent.parent.child('text').exists()"
      }
    }
  }
}     

The problem with this is that a multi-path update performs one big check for the security rules across all these updates. So if one node was deleted, the updating of all names will fail. If that is what you want, I'd go for that.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thanks for the input. Tried this option but keep getting "Invalid property access: target is not an object" error after removing semicolon because it caused "Expected ',' or '}'." But anyways I think it does not make sense following this route, since I don't want it to fail updating the other usernames. How would you suggest attacking this? Goal: Update all usernames, but while doing that not create new structure (or preventing it from happening) – Bot Raid May 22 '20 at 23:03
  • I'd update them iteratively, instead of in a single multi-location update. Also see https://stackoverflow.com/questions/30693785/how-to-write-denormalized-data-in-firebase – Frank van Puffelen May 23 '20 at 01:06
  • Alright, this sortByChild query is a good idea, however I cannot really do it in my case since I have many recipes in which I have to check all comments of uncertain amount for the username. This cannot be solved via a sortByChild, can it? On the other hand another very inefficient method would be, to check all recipes on the index for their existence and if so, update them via Multi-path. Would you recommend that or does something else come to your mind? – Bot Raid May 23 '20 at 08:29