Sauron
Sauron

Reputation: 6647

How to properly denormalize data in Firebase

I am very new to NoSQL and denormalization. However, I wish to allow the actions at SignUp within my app defined as:

  1. If a username is already taken, then a user is not allowed to use it
  2. If a phone number is already taken, then a user is not allowed to use it
  3. Allow a new user to "sync" their phone number contacts with the server to determine who are presently users, and retrieve their respective uid's

I have the schema outlined as below given the quick need to check if a username/phone number is already present at user sign up, as well as the needed search and compare given if the new users contacts phone numbers are link to users already present within the app:

{
  "presentUsersByPhoneNumber" : {
    "1614#######" : {
      "uid" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
    },
    "1614#######" : {
      "uid" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
    },
    "1614#######" : {
      "uid" : "1783917f-00e4-47a0-b2cd-987bdf185129"
    },
    "1614#######" : {
      "uid" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
    }
  },
  "presentUsersByUsername" : {
    "ak" : {
      "uid" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
    },
    "ak2" : {
      "uid" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
    },
    "ak3" : {
      "uid" : "1783917f-00e4-47a0-b2cd-987bdf185129"
    },
    "kja" : {
      "uid" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
    }
  },
  "users" : {
    "1783917f-00e4-47a0-b2cd-987bdf185129" : {
      "phoneNumber" : "614#######",
      "username" : "ak3"
    },
    "99e4989b-a046-4c5f-9478-5ebd8bdc3ded" : {
      "phoneNumber" : "1614#######",
      "username" : "ak2"
    },
    "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd" : {
      "phoneNumber" : "1614#######",
      "username" : "ak1"
    },
    "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de" : {
      "phoneNumber" : "1614#######",
      "username" : "kja"
    }
  }
}

Is this approach going too fair in the act of denormalizaiton?

Upvotes: 1

Views: 391

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 598728

In NoSQL you should model the data for how your application needs to access it. Read this article on NoSQL data modeling for more information.

So if you need an efficient way to check whether a phone number or username is already taken, it makes sense to store mappings for those. The only thing I would probably do different there is to store them as simple types:

"phoneNumberToUid" : {
    "1614#######" : "fdb17f3a-7b7d-4aa5-9a0b-b9fb33c349de"
    "1614#######" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
},
"usernameToUid" : {
    "ak" : "a96da7b1-7c4e-44bc-b82e-fc75bed52bcd"
    "ak2" : "99e4989b-a046-4c5f-9478-5ebd8bdc3ded"
}

One thing I noticed in your sample data is that you have a key ak in presentUsersByUsername, but there is no corresponding child in users with that name. This typically happens because your code either aborts half-way through or because you made a mistake at some point during development.

You can prevent many of these problems by:

  1. using multi-location updates, so that all writes are sent to Firebase as a single command

    ref.update({
      '/users/a96da7b1-7c4e-44bc-b82e-fc75bed52bcd/username': 'ak1',
      '/usernameToUid/ak': null,
      '/usernameToUid/ak1': 'a96da7b1-7c4e-44bc-b82e-fc75bed52bcd'
    });
    

    This update is safest way to change the name from the user from ak to ak1, wiping the old mapping and adding a new one.

  2. using validation rules ensure that a user for each name exists

    "usernameToUid": {
      "$username": {
        ".validate": "newData.parent().parent().child(newData.va()).child('username').val() == $username"
      }
    }
    

Upvotes: 1

Related Questions