I would like to create data that scales (to track private data of a user).
The Firebase documentation recommends to nest the child objects under the parent like this:
{
"users": {
"google:1234567890": {
"displayName" : "Username A",
"provider" : "google",
"provider_id" : "1234567890",
"todos": {
"rec_id1": "Walk the dog",
"rec_id2": "Buy milk",
"rec_id3": "Win a gold medal in the Olympics",
...
}
}, ...
}
}
(Where rec_id is a unique key generated by Firebase.push().)
But as mentioned in Denormalizing Your Data is Normal I think it would be better to structure it this way:
{
"users" : {
"google:1234567890" : {
"displayName" : "Username A",
"provider" : "google",
"provider_id" : "1234567890"
}, ...
},
"todos" : {
"google:1234567890" : {
"rec_id1" : {
"todo" : "Walk the dog"
},
"rec_id2" : {
"todo" : "Buy milk"
},
"rec_id3" : {
"todo" : "Win a gold medal in the Olympics"
}, ...
}, ...
}
}
And then to only allow the user to write/read it's own data apply the following security rules:
{
"rules": {
"users": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.uid === $uid"
}
},
"todos": {
"$uid": {
// grants write and read access to the owner of this user account whose uid must exactly match the key ($uid)
".write": "auth !== null && auth.uid === $uid",
".read": "auth !== null && auth.uid === $uid"
}
}
}
}
As I'm new to this kind of databases I wonder if there are any downsides the way I'd like to structure it.
Would it be better to nest all todos directly under the user as recommended in the first example?
First, a couple resources if you haven't run into them yet:
EDIT: You have obviously ran into these resources since it's linked in your question, but I would suggest re-reading over the Structuring Your Data guide a few more times.
That brings us to your scenario...
The two ways you have your data set up actually accomplish almost the same thing!
You'll notice though, that the first example is actually listed as an anti-pattern in the "Structuring Your Data" guide.
/users/$userUid
gives me the user data and/history/$userUid
gives me the user's history.The different approach is:
/todos/$uid
, you can push a newtodo
object to/todos
so that it gets a new unique ID (called key).user
object'stodos
child.todos
the user belongs to independently.Doing it this way would:
todo
, without having to update it's child parameters across multiple location.Here is the last data sample from the "Creating Data That Scales" section of the guide: (with some comments I added)
This achieves a flatter structure.
As the documentation says,
So the question is, how could your data look in order to allow a user to have multiple todo lists which can be shared with other users as well?
Here is an example:
/todoList
Firebase.push()
.In conclusion, it all really comes down to how and when your app needs your data, how often data is updated and by whom, and also to minimizing unnecessary reads and watchers. Space is usually cheap, operations (and watchers) usually aren't.
Last but not least, rules and security are another extremely important consideration. The last part of the guide says:
It's early and I hope I'm not blabbering, but I ran into these same questions when I first went from only knowing MySql to using unstructured, so I hope that helps!