8

My database uses redundant data to speed up fetches and minimise the number of documents that need to be read for certain queries. For example I'd store the names of followed users in a map in a users document so I don't have to read another document to retrieve the names of each of the followed users.

User: (Collection) {
    userID: (Document) {

    //user state
    name: ...

    followingUsers: (Map) {
        followingUserID: nameOfUser,
        followingUserID: nameOfUser
    }
}

}

If a user was to change their name, what is the best way to propagate these changes to all places with the redundant data?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
iCode101
  • 395
  • 2
  • 17
  • Great question! I wrote the basic approaches for Firebase's Realtime Database a while ago in [this answer](https://stackoverflow.com/a/30699277/209103). White Firestore is a different database, the approaches are likely to be the same. – Frank van Puffelen Jan 12 '18 at 14:37

1 Answers1

7

Good question! For starters, I'd recommend doing this kind of administrative task in a server SDK or cloud function, since you don't want a client to necessarily have the ability to start mucking with every single User doc.

The good news is that, once you start using the server SDKs, you can then put a query into a transaction. So let's say user_123 changes their name from "Jenny" to "Jen". Your transaction would look something like this in pseudo-code:

  • Start Transaction
    • transaction.get(usersRef.where("followingUsers.user_123", ">=", ""))
    • Loop through query results. Grab the doc_id from each doc and use that to start building out the writes in your transaction.
      • transaction.update("/users/<doc_id>/", {"followingUsers.user_123" : "Jen"})
    • Also make sure you add transactions.update("/users/user_123", {"name": "Jen"})
  • End transaction

This general approach would also work on the client-side, but you just wouldn't be able to do this in a transaction. (You could still put all of these changes into a batch write, though.)

Todd Kerpelman
  • 16,875
  • 4
  • 42
  • 40
  • Thanks for your answer. I see why it would be a bad idea to give clients write privileges to other User docs, but hypothetically why couldn't this transaction be done client side? – iCode101 Jan 12 '18 at 20:51
  • If the operation doesn't require the current value of any pre-existing data, use a [batched write](https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes) instead of a transaction, since it's simpler and has less chance of problems. – Frank van Puffelen Jan 13 '18 at 03:21
  • 1
    And yes: the transaction (or batched) write could also be done from the client. You may want to enforce the data structure in your security rules in that case. While this is possible, it is often more (unfamiliar) work than doing the same in Cloud Functions as Todd recommended. – Frank van Puffelen Jan 13 '18 at 03:22
  • iCode, the only reason this can't be done client-side is that the client-side SDK doesn't have the ability to perform a query inside of a transaction. (You can only do individual document reads) – Todd Kerpelman Jan 16 '18 at 15:56