Learn how to model and structure data relationships with the Firestore NoSQL Database. We build simple one-to-many and many-to-many relationships by modeling a  twitter-inspired app. https://angularfirebase.com/lessons/firestore-nosql-data-modeling-by-example

- Firestore Structuring: https://firebase.google.com/docs/firestore/manage-data/structure-data
- NoSQL Document DB - https://en.wikipedia.org/wiki/Document-oriented_database
2024-02-04T07:46:34.129Z
nova-2
2-general-nova
2024-01-09.29447
30089e05-99d1-4376-b32e-c263170674af
ca4d5586-e08b-4413-82a2-6ef6c497f052
bfd69a0c496d236eda2b240d356938539879398c0344d6c1c570e79986a25683
67875a7f-c9c4-48a0-aa55-5bdb8a91c34a
deprecated
Modeling relational data in a NoSQL database like Firestore can be a challenge, especially if you come from a SQL background because you'll have to unlearn many of the principles that you already know.
My goal today is to show you a handful of techniques that you can use to model data relationships, such as one to 1, one to many, and many to many.
Because NoSQL is schema less, we have the flexibility to model these relationships in a variety of different ways.
I'm going to show you how to build these data models in the Firestore console, and we'll talk about the trade offs for each technique.
If you're new here, make sure to like and subscribe and follow along with the source code at angularfirebase.com.
Firestore is a NoSQL document oriented database similar to CouchDB and MongoDB if you're Familiar with those.
Instead of storing data with a bunch of columns and rows like you would with a traditional SQL database, you store data as collections of documents.
Collections don't store any data of their own.
They're just containers to hold multiple documents that you can query.
And a document itself can just be thought of as a JSON object.
In order to keep your queries efficient, it's important that you make your collections large and your documents small.
In fact, documents need to be at least 1 megabyte or smaller.
But a collection can hold millions of documents and still be queried efficiently.
Firestore gives you 3 fundamental ways to model your data.
The first way would be a root collection, which is just a collection of documents in the root of your database.
The second option is to embed data directly on the document itself.
It behaves very similar to JSON, so we can save objects, arrays, and other data types on the document.
It just needs to be relatively small, again, limited to 1 megabyte.
Then our third option is to nest a subcollection on a document.
This allows us to scope collections to a parent document, creating a hierarchy structure.
Now that we know the fundamentals, let's jump into the Firestore console and start modeling some data.
The first thing we'll do is create a root collection called users that maintains a document for each user in our app.
Every document in a collection needs to have a unique ID, but you can create your own ID.
And there are some clever things you can do to manage relationships by doing that, which we'll get into here in a second.
Now let's assume that every user in our app might be able to obtain certain in rankings or achievements through activity that they perform in our app.
In other words, a user has many rankings.
The easiest way to maintain this relationship is by embedding an object on the document itself.
So we'll call that rankings and select an object.
And then for each relationship, we set a key and a value.
You can make queries based on these object properties, so it's important to use consistent naming across all user documents.
I'm just using a number as the value, but you could nest an additional object or other data type under each property.
That works pretty well, but it It doesn't scale if your anticipated data footprint is any greater than a megabyte.
So embedded data works best when you have a small simple Data structure that doesn't need to scale up to thousands of records.
Let's go ahead and assume our ranking data is much more complex and we'll nest it as a subcollection.
We can click add collection, which we'll call rankings, then we'll quickly add a couple of documents to the sub collection.
The nice thing here is that we can save as many documents in Collection is we need.
And it also maintains the relationship with the parent document, which happens to be the user.
That's great for representing the relationship, But you need to ask yourself, are you going to need to query this data across any parent documents?
In other words, do you need to make a query that goes something like I need all users that are ranked black belt because currently, that type of query is not possible in a subcollection.
The data is completely scoped to that user, so you can't use it to query across multiple users.
So what we're going to need to do in that case is denormalize our data and save our rankings collection as a root collection.
When we create a document in this root collection, we'll want to save the user ID of the associated user on each individual document.
You can embed a document ID as a string or the actual full path to a document in Firestore.
In most cases, saving the document ID as a string will be sufficient, but there may be cases where you have deeply nested complex data that would benefit from having the full document reference.
In either case, the idea is very similar to a foreign key in a SQL database.
We can't do join queries like we do in a SQL database, But what we can do is reference a related document and pull that in as a secondary query.
With the root collection, we can also reference multiple user IDs to create a many to many relationship.
By adding a second user ID, what I'm doing here is creating a relationship that says a ranking can have many users and users can have many rankings.
Now let's try another example.
Let's see if we can model the relationship between tweets and users in Twitter in Firestore.
So the first thing I might do is create a sub collection of tweets under each user document.
A tweet is owned by a user, so logically, you would think that a tweet should be nested under the user.
When displaying tweets for a given user's feed, that should be perfectly fine.
But the one caveat there is that if we want to display a feed that goes across multiple users, we're going to have a problem.
Every query to this tweets collection is going to be scoped to that user document.
So let's imagine we want all of a user's tweets for a certain day.
We could say where created at is greater than or equal to today.
That works well in certain cases.
But in this case, since we want to query all of the tweets across all users, we're going to need to make this a root collection.
That will allow us to modify our query to be scoped at the root instead of underneath this user document.
We could also try to embed a tweets object on the user document directly.
But Since our users can potentially have thousands of tweets, that doesn't really make sense from a scaling standpoint.
So we'll go ahead and move that collection to the root, And this time, we'll save the user ID on each individual tweet document.
We lose the convenient relational structure of a subcollection, But in return, we get a more flexible structure for compound queries.
Now we can write code that's going to return all the tweets for multiple users, for example, all the tweets created today.
But we can still scope the tweets to the exact user by querying that user ID.
So we'll say ref where user ID equals user 1.
And we can even chain these where statements together to perform a compound query.
So that gives us a nice one to many relationship between users and tweets, but users can also like other users' tweets.
So how might we set that up?
That gives us a many to many relationship where a heart belongs to both a user and a tweet.
And we also want that relationship to be unique.
For example, a user shouldn't be able to heart the same post multiple times.
So one easy way to enforce that uniqueness is to create your own custom document ID.
So in this example, I have a heart document where the ID is the user ID followed by an underscore followed by the tweet ID.
By doing that, it becomes easy to query a specific tweet to see if a user has already hearted that tweet, and it also ensures that a user can't heart a tweet more than once.
If you have a SQL background, this technique is very similar to using an intermediate table for joins.
If we have a User ID and a tweet ID in our front end app, we can simply use those to request the corresponding document to see if the user has already liked a tweet.
Then we could get the total heart count by querying the hearts collection with that tweet ID, but this does pose one major problem.
If a tweet has been liked 1,000 or millions of times, we're going to have to query that entire collection just to get a simple count.
To overcome that problem, what we wanna do is Aggregate our data as it's created.
That just means we sum up the total hearts every time a new one is created and save it on the tweet document in the background using Firebase Cloud Functions.
It's surprisingly simple and I show you exactly how to do it in episode 63.
But the general idea is that we're going to need to show the Count on a tweet very often.
And we don't wanna read that entire collection over and over again.
So we set up a Cloud Function that runs whenever a new heart document is created, Then we reference that tweet and increment or decrement its total heart count.
That allows us to perform a single document read when showing Count in the front end, which is going to be a lot faster, less expensive, and it's the only way you can scale.
I'm gonna go ahead and wrap up there.
If you're a pro member, be looking out For a more advanced data modeling video in a couple days to answer some of the questions that you posted on Slack, I hope this video gave you some basic ideas on how you can model relationships in Firestore.
If it helped you, please like and subscribe.
And if you wanna get my advice for modeling your own complex data structures, consider becoming a pro member and drop me a line on Slack.
You'll get a free copy of my book as well as 1 on 1 project support.
Thanks for watching, and I'll see you soon.

Speaker 0: Modeling relational data in a NoSQL database like Firestore can be a challenge, especially if you come from a SQL background because you'll have to unlearn many of the principles that you already know. My goal today is to show you a handful of techniques that you can use to model data relationships, such as one to 1, one to many, and many to many. Because NoSQL is schema less, we have the flexibility to model these relationships in a variety of different ways. I'm going to show you how to build these data models in the Firestore console, and we'll talk about the trade offs for each technique. If you're new here, make sure to like and subscribe and follow along with the source code at angularfirebase.com.

Firestore is a NoSQL document oriented database similar to CouchDB and MongoDB if you're Familiar with those. Instead of storing data with a bunch of columns and rows like you would with a traditional SQL database, you store data as collections of documents. Collections don't store any data of their own. They're just containers to hold multiple documents that you can query. And a document itself can just be thought of as a JSON object.

In order to keep your queries efficient, it's important that you make your collections large and your documents small. In fact, documents need to be at least 1 megabyte or smaller. But a collection can hold millions of documents and still be queried efficiently. Firestore gives you 3 fundamental ways to model your data. The first way would be a root collection, which is just a collection of documents in the root of your database.

The second option is to embed data directly on the document itself. It behaves very similar to JSON, so we can save objects, arrays, and other data types on the document. It just needs to be relatively small, again, limited to 1 megabyte. Then our third option is to nest a subcollection on a document. This allows us to scope collections to a parent document, creating a hierarchy structure.

Now that we know the fundamentals, let's jump into the Firestore console and start modeling some data. The first thing we'll do is create a root collection called users that maintains a document for each user in our app. Every document in a collection needs to have a unique ID, but you can create your own ID. And there are some clever things you can do to manage relationships by doing that, which we'll get into here in a second. Now let's assume that every user in our app might be able to obtain certain in rankings or achievements through activity that they perform in our app.

In other words, a user has many rankings. The easiest way to maintain this relationship is by embedding an object on the document itself. So we'll call that rankings and select an object. And then for each relationship, we set a key and a value. You can make queries based on these object properties, so it's important to use consistent naming across all user documents.

I'm just using a number as the value, but you could nest an additional object or other data type under each property. That works pretty well, but it It doesn't scale if your anticipated data footprint is any greater than a megabyte. So embedded data works best when you have a small simple Data structure that doesn't need to scale up to thousands of records. Let's go ahead and assume our ranking data is much more complex and we'll nest it as a subcollection. We can click add collection, which we'll call rankings, then we'll quickly add a couple of documents to the sub collection.

The nice thing here is that we can save as many documents in Collection is we need. And it also maintains the relationship with the parent document, which happens to be the user. That's great for representing the relationship, But you need to ask yourself, are you going to need to query this data across any parent documents? In other words, do you need to make a query that goes something like I need all users that are ranked black belt because currently, that type of query is not possible in a subcollection. The data is completely scoped to that user, so you can't use it to query across multiple users.

So what we're going to need to do in that case is denormalize our data and save our rankings collection as a root collection. When we create a document in this root collection, we'll want to save the user ID of the associated user on each individual document. You can embed a document ID as a string or the actual full path to a document in Firestore. In most cases, saving the document ID as a string will be sufficient, but there may be cases where you have deeply nested complex data that would benefit from having the full document reference. In either case, the idea is very similar to a foreign key in a SQL database.

We can't do join queries like we do in a SQL database, But what we can do is reference a related document and pull that in as a secondary query. With the root collection, we can also reference multiple user IDs to create a many to many relationship. By adding a second user ID, what I'm doing here is creating a relationship that says a ranking can have many users and users can have many rankings. Now let's try another example. Let's see if we can model the relationship between tweets and users in Twitter in Firestore.

So the first thing I might do is create a sub collection of tweets under each user document. A tweet is owned by a user, so logically, you would think that a tweet should be nested under the user. When displaying tweets for a given user's feed, that should be perfectly fine. But the one caveat there is that if we want to display a feed that goes across multiple users, we're going to have a problem. Every query to this tweets collection is going to be scoped to that user document.

So let's imagine we want all of a user's tweets for a certain day. We could say where created at is greater than or equal to today. That works well in certain cases. But in this case, since we want to query all of the tweets across all users, we're going to need to make this a root collection. That will allow us to modify our query to be scoped at the root instead of underneath this user document.

We could also try to embed a tweets object on the user document directly. But Since our users can potentially have thousands of tweets, that doesn't really make sense from a scaling standpoint. So we'll go ahead and move that collection to the root, And this time, we'll save the user ID on each individual tweet document. We lose the convenient relational structure of a subcollection, But in return, we get a more flexible structure for compound queries. Now we can write code that's going to return all the tweets for multiple users, for example, all the tweets created today.

But we can still scope the tweets to the exact user by querying that user ID. So we'll say ref where user ID equals user 1. And we can even chain these where statements together to perform a compound query. So that gives us a nice one to many relationship between users and tweets, but users can also like other users' tweets. So how might we set that up?

That gives us a many to many relationship where a heart belongs to both a user and a tweet. And we also want that relationship to be unique. For example, a user shouldn't be able to heart the same post multiple times. So one easy way to enforce that uniqueness is to create your own custom document ID. So in this example, I have a heart document where the ID is the user ID followed by an underscore followed by the tweet ID.

By doing that, it becomes easy to query a specific tweet to see if a user has already hearted that tweet, and it also ensures that a user can't heart a tweet more than once. If you have a SQL background, this technique is very similar to using an intermediate table for joins. If we have a User ID and a tweet ID in our front end app, we can simply use those to request the corresponding document to see if the user has already liked a tweet. Then we could get the total heart count by querying the hearts collection with that tweet ID, but this does pose one major problem. If a tweet has been liked 1,000 or millions of times, we're going to have to query that entire collection just to get a simple count.

To overcome that problem, what we wanna do is Aggregate our data as it's created. That just means we sum up the total hearts every time a new one is created and save it on the tweet document in the background using Firebase Cloud Functions. It's surprisingly simple and I show you exactly how to do it in episode 63. But the general idea is that we're going to need to show the Count on a tweet very often. And we don't wanna read that entire collection over and over again.

So we set up a Cloud Function that runs whenever a new heart document is created, Then we reference that tweet and increment or decrement its total heart count. That allows us to perform a single document read when showing Count in the front end, which is going to be a lot faster, less expensive, and it's the only way you can scale. I'm gonna go ahead and wrap up there. If you're a pro member, be looking out For a more advanced data modeling video in a couple days to answer some of the questions that you posted on Slack, I hope this video gave you some basic ideas on how you can model relationships in Firestore. If it helped you, please like and subscribe.

And if you wanna get my advice for modeling your own complex data structures, consider becoming a pro member and drop me a line on Slack. You'll get a free copy of my book as well as 1 on 1 project support. Thanks for watching, and I'll see you soon.
Modeling relational data in a NoSQL database like Firestore can be a challenge, especially if you come from a SQL background because you'll have to unlearn many of the principles that you already know. My goal today is to show you a handful of techniques that you can use to model data relationships, such as one to 1, one to many, and many to many. Because NoSQL is schema less, we have the flexibility to model these relationships in a variety of different ways. I'm going to show you how to build these data models in the Firestore console, and we'll talk about the trade offs for each technique. If you're new here, make sure to like and subscribe and follow along with the source code at angularfirebase.com. Firestore is a NoSQL document oriented database similar to CouchDB and MongoDB if you're Familiar with those. Instead of storing data with a bunch of columns and rows like you would with a traditional SQL database, you store data as collections of documents. Collections don't store any data of their own. They're just containers to hold multiple documents that you can query. And a document itself can just be thought of as a JSON object. In order to keep your queries efficient, it's important that you make your collections large and your documents small. In fact, documents need to be at least 1 megabyte or smaller. But a collection can hold millions of documents and still be queried efficiently. Firestore gives you 3 fundamental ways to model your data. The first way would be a root collection, which is just a collection of documents in the root of your database. The second option is to embed data directly on the document itself.
databases
data
data analysis
modeling
data management
websites
documentation
It behaves very similar to JSON, so we can save objects, arrays, and other data types on the document. It just needs to be relatively small, again, limited to 1 megabyte. Then our third option is to nest a subcollection on a document. This allows us to scope collections to a parent document, creating a hierarchy structure. Now that we know the fundamentals, let's jump into the Firestore console and start modeling some data. The first thing we'll do is create a root collection called users that maintains a document for each user in our app. Every document in a collection needs to have a unique ID, but you can create your own ID. And there are some clever things you can do to manage relationships by doing that, which we'll get into here in a second.
Now let's assume that every user in our app might be able to obtain certain in rankings or achievements through activity that they perform in our app. In other words, a user has many rankings. The easiest way to maintain this relationship is by embedding an object on the document itself. So we'll call that rankings and select an object. And then for each relationship, we set a key and a value. You can make queries based on these object properties, so it's important to use consistent naming across all user documents.
google
data management
I'm just using a number as the value, but you could nest an additional object or other data type under each property. That works pretty well, but it It doesn't scale if your anticipated data footprint is any greater than a megabyte. So embedded data works best when you have a small simple Data structure that doesn't need to scale up to thousands of records. Let's go ahead and assume our ranking data is much more complex and we'll nest it as a subcollection. We can click add collection, which we'll call rankings, then we'll quickly add a couple of documents to the sub collection.
data
data management
databases
data analysis
The nice thing here is that we can save as many documents in Collection is we need.
And it also maintains the relationship with the parent document, which happens to be the user. That's great for representing the relationship, But you need to ask yourself, are you going to need to query this data across any parent documents? In other words, do you need to make a query that goes something like I need all users that are ranked black belt because currently, that type of query is not possible in a subcollection. The data is completely scoped to that user, so you can't use it to query across multiple users. So what we're going to need to do in that case is denormalize our data and save our rankings collection as a root collection. When we create a document in this root collection, we'll want to save the user ID of the associated user on each individual document.
data management
databases
You can embed a document ID as a string or the actual full path to a document in Firestore.
In most cases, saving the document ID as a string will be sufficient, but there may be cases where you have deeply nested complex data that would benefit from having the full document reference. In either case, the idea is very similar to a foreign key in a SQL database. We can't do join queries like we do in a SQL database, But what we can do is reference a related document and pull that in as a secondary query. With the root collection, we can also reference multiple user IDs to create a many to many relationship.
databases
By adding a second user ID, what I'm doing here is creating a relationship that says a ranking can have many users and users can have many rankings.
Now let's try another example. Let's see if we can model the relationship between tweets and users in Twitter in Firestore. So the first thing I might do is create a sub collection of tweets under each user document. A tweet is owned by a user, so logically, you would think that a tweet should be nested under the user. When displaying tweets for a given user's feed, that should be perfectly fine. But the one caveat there is that if we want to display a feed that goes across multiple users, we're going to have a problem. Every query to this tweets collection is going to be scoped to that user document. So let's imagine we want all of a user's tweets for a certain day. We could say where created at is greater than or equal to today. That works well in certain cases. But in this case, since we want to query all of the tweets across all users, we're going to need to make this a root collection. That will allow us to modify our query to be scoped at the root instead of underneath this user document. We could also try to embed a tweets object on the user document directly. But Since our users can potentially have thousands of tweets, that doesn't really make sense from a scaling standpoint. So we'll go ahead and move that collection to the root, And this time, we'll save the user ID on each individual tweet document. We lose the convenient relational structure of a subcollection, But in return, we get a more flexible structure for compound queries. Now we can write code that's going to return all the tweets for multiple users, for example, all the tweets created today. But we can still scope the tweets to the exact user by querying that user ID. So we'll say ref where user ID equals user 1. And we can even chain these where statements together to perform a compound query. So that gives us a nice one to many relationship between users and tweets, but users can also like other users' tweets. So how might we set that up? That gives us a many to many relationship where a heart belongs to both a user and a tweet.
twitter
social media
And we also want that relationship to be unique. For example, a user shouldn't be able to heart the same post multiple times. So one easy way to enforce that uniqueness is to create your own custom document ID.
So in this example, I have a heart document where the ID is the user ID followed by an underscore followed by the tweet ID. By doing that, it becomes easy to query a specific tweet to see if a user has already hearted that tweet, and it also ensures that a user can't heart a tweet more than once. If you have a SQL background, this technique is very similar to using an intermediate table for joins. If we have a User ID and a tweet ID in our front end app, we can simply use those to request the corresponding document to see if the user has already liked a tweet.
twitter
databases
Then we could get the total heart count by querying the hearts collection with that tweet ID, but this does pose one major problem. If a tweet has been liked 1,000 or millions of times, we're going to have to query that entire collection just to get a simple count.
To overcome that problem, what we wanna do is Aggregate our data as it's created. That just means we sum up the total hearts every time a new one is created and save it on the tweet document in the background using Firebase Cloud Functions. It's surprisingly simple and I show you exactly how to do it in episode 63. But the general idea is that we're going to need to show the Count on a tweet very often. And we don't wanna read that entire collection over and over again. So we set up a Cloud Function that runs whenever a new heart document is created, Then we reference that tweet and increment or decrement its total heart count.
data
twitter
That allows us to perform a single document read when showing Count in the front end, which is going to be a lot faster, less expensive, and it's the only way you can scale. I'm gonna go ahead and wrap up there. If you're a pro member, be looking out For a more advanced data modeling video in a couple days to answer some of the questions that you posted on Slack, I hope this video gave you some basic ideas on how you can model relationships in Firestore. If it helped you, please like and subscribe. And if you wanna get my advice for modeling your own complex data structures, consider becoming a pro member and drop me a line on Slack. You'll get a free copy of my book as well as 1 on 1 project support. Thanks for watching, and I'll see you soon.
books
modeling
data management
literature
data
data analysis
customer support
streaming
publishing
reading
Modeling relational data in a NoSQL database like Firestore can be a challenge, especially if you come from a SQL background because you'll have to unlearn many of the principles that you already know. My goal today is to show you a handful of techniques that you can use to model data relationships, such as one to 1, one to many, and many to many. Because NoSQL is schema less, we have the flexibility to model these relationships in a variety of different ways. I'm going to show you how to build these data models in the Firestore console, and we'll talk about the trade offs for each technique. If you're new here, make sure to like and subscribe and follow along with the source code at angularfirebase.com. Firestore is a NoSQL document oriented database similar to CouchDB and MongoDB if you're Familiar with those. Instead of storing data with a bunch of columns and rows like you would with a traditional SQL database, you store data as collections of documents. Collections don't store any data of their own. They're just containers to hold multiple documents that you can query. And a document itself can just be thought of as a JSON object. In order to keep your queries efficient, it's important that you make your collections large and your documents small. In fact, documents need to be at least 1 megabyte or smaller. But a collection can hold millions of documents and still be queried efficiently. Firestore gives you 3 fundamental ways to model your data. The first way would be a root collection, which is just a collection of documents in the root of your database. The second option is to embed data directly on the document itself. It behaves very similar to JSON, so we can save objects, arrays, and other data types on the document. It just needs to be relatively small, again, limited to 1 megabyte. Then our third option is to nest a subcollection on a document. This allows us to scope collections to a parent document, creating a hierarchy structure. Now that we know the fundamentals, let's jump into the Firestore console and start modeling some data. The first thing we'll do is create a root collection called users that maintains a document for each user in our app. Every document in a collection needs to have a unique ID, but you can create your own ID. And there are some clever things you can do to manage relationships by doing that, which we'll get into here in a second. Now let's assume that every user in our app might be able to obtain certain in rankings or achievements through activity that they perform in our app. In other words, a user has many rankings. The easiest way to maintain this relationship is by embedding an object on the document itself. So we'll call that rankings and select an object. And then for each relationship, we set a key and a value. You can make queries based on these object properties, so it's important to use consistent naming across all user documents. I'm just using a number as the value, but you could nest an additional object or other data type under each property. That works pretty well, but it It doesn't scale if your anticipated data footprint is any greater than a megabyte. So embedded data works best when you have a small simple Data structure that doesn't need to scale up to thousands of records. Let's go ahead and assume our ranking data is much more complex and we'll nest it as a subcollection. We can click add collection, which we'll call rankings, then we'll quickly add a couple of documents to the sub collection. The nice thing here is that we can save as many documents in Collection is we need. And it also maintains the relationship with the parent document, which happens to be the user. That's great for representing the relationship, But you need to ask yourself, are you going to need to query this data across any parent documents? In other words, do you need to make a query that goes something like I need all users that are ranked black belt because currently, that type of query is not possible in a subcollection. The data is completely scoped to that user, so you can't use it to query across multiple users. So what we're going to need to do in that case is denormalize our data and save our rankings collection as a root collection. When we create a document in this root collection, we'll want to save the user ID of the associated user on each individual document. You can embed a document ID as a string or the actual full path to a document in Firestore. In most cases, saving the document ID as a string will be sufficient, but there may be cases where you have deeply nested complex data that would benefit from having the full document reference. In either case, the idea is very similar to a foreign key in a SQL database. We can't do join queries like we do in a SQL database, But what we can do is reference a related document and pull that in as a secondary query. With the root collection, we can also reference multiple user IDs to create a many to many relationship. By adding a second user ID, what I'm doing here is creating a relationship that says a ranking can have many users and users can have many rankings. Now let's try another example. Let's see if we can model the relationship between tweets and users in Twitter in Firestore. So the first thing I might do is create a sub collection of tweets under each user document. A tweet is owned by a user, so logically, you would think that a tweet should be nested under the user. When displaying tweets for a given user's feed, that should be perfectly fine. But the one caveat there is that if we want to display a feed that goes across multiple users, we're going to have a problem. Every query to this tweets collection is going to be scoped to that user document. So let's imagine we want all of a user's tweets for a certain day. We could say where created at is greater than or equal to today. That works well in certain cases. But in this case, since we want to query all of the tweets across all users, we're going to need to make this a root collection. That will allow us to modify our query to be scoped at the root instead of underneath this user document. We could also try to embed a tweets object on the user document directly. But Since our users can potentially have thousands of tweets, that doesn't really make sense from a scaling standpoint. So we'll go ahead and move that collection to the root, And this time, we'll save the user ID on each individual tweet document. We lose the convenient relational structure of a subcollection, But in return, we get a more flexible structure for compound queries. Now we can write code that's going to return all the tweets for multiple users, for example, all the tweets created today. But we can still scope the tweets to the exact user by querying that user ID. So we'll say ref where user ID equals user 1. And we can even chain these where statements together to perform a compound query. So that gives us a nice one to many relationship between users and tweets, but users can also like other users' tweets. So how might we set that up? That gives us a many to many relationship where a heart belongs to both a user and a tweet. And we also want that relationship to be unique. For example, a user shouldn't be able to heart the same post multiple times. So one easy way to enforce that uniqueness is to create your own custom document ID. So in this example, I have a heart document where the ID is the user ID followed by an underscore followed by the tweet ID. By doing that, it becomes easy to query a specific tweet to see if a user has already hearted that tweet, and it also ensures that a user can't heart a tweet more than once. If you have a SQL background, this technique is very similar to using an intermediate table for joins. If we have a User ID and a tweet ID in our front end app, we can simply use those to request the corresponding document to see if the user has already liked a tweet. Then we could get the total heart count by querying the hearts collection with that tweet ID, but this does pose one major problem. If a tweet has been liked 1,000 or millions of times, we're going to have to query that entire collection just to get a simple count. To overcome that problem, what we wanna do is Aggregate our data as it's created. That just means we sum up the total hearts every time a new one is created and save it on the tweet document in the background using Firebase Cloud Functions. It's surprisingly simple and I show you exactly how to do it in episode 63. But the general idea is that we're going to need to show the Count on a tweet very often. And we don't wanna read that entire collection over and over again. So we set up a Cloud Function that runs whenever a new heart document is created, Then we reference that tweet and increment or decrement its total heart count. That allows us to perform a single document read when showing Count in the front end, which is going to be a lot faster, less expensive, and it's the only way you can scale. I'm gonna go ahead and wrap up there. If you're a pro member, be looking out For a more advanced data modeling video in a couple days to answer some of the questions that you posted on Slack, I hope this video gave you some basic ideas on how you can model relationships in Firestore. If it helped you, please like and subscribe. And if you wanna get my advice for modeling your own complex data structures, consider becoming a pro member and drop me a line on Slack. You'll get a free copy of my book as well as 1 on 1 project support. Thanks for watching, and I'll see you soon.
Modeling
modeling
relational
relational
data
data
in
in
a
a
NoSQL
nosql
database
database
like
like
Firestore
firestore
can
can
be
be
a
a
challenge,
challenge
especially
especially
if
if
you
you
come
come
from
from
a
a
SQL
sql
background
background
because
because
you'll
you'll
have
have
to
to
unlearn
unlearn
many
many
of
of
the
the
principles
principles
that
that
you
you
already
already
know.
know
My
my
goal
goal
today
today
is
is
to
to
show
show
you
you
a
a
handful
handful
of
of
techniques
techniques
that
that
you
you
can
can
use
use
to
to
model
model
data
data
relationships,
relationships
such
such
as
as
one
one
to
to
1,
1
one
one
to
to
many,
many
and
and
many
many
to
to
many.
many
Because
because
NoSQL
nosql
is
is
schema
schema
less,
less
we
we
have
have
the
the
flexibility
flexibility
to
to
model
model
these
these
relationships
relationships
in
in
a
a
variety
variety
of
of
different
different
ways.
ways
I'm
i'm
going
going
to
to
show
show
you
you
how
how
to
to
build
build
these
these
data
data
models
models
in
in
the
the
Firestore
firestore
console,
console
and
and
we'll
we'll
talk
talk
about
about
the
the
trade
trade
offs
offs
for
for
each
each
technique.
technique
If
if
you're
you're
new
new
here,
here
make
make
sure
sure
to
to
like
like
and
and
subscribe
subscribe
and
and
follow
follow
along
along
with
with
the
the
source
source
code
code
at
at
angularfirebase.com.
angularfirebase.com
Firestore
firestore
is
is
a
a
NoSQL
nosql
document
document
oriented
oriented
database
database
similar
similar
to
to
CouchDB
couchdb
and
and
MongoDB
mongodb
if
if
you're
you're
Familiar
familiar
with
with
those.
those
Instead
instead
of
of
storing
storing
data
data
with
with
a
a
bunch
bunch
of
of
columns
columns
and
and
rows
rows
like
like
you
you
would
would
with
with
a
a
traditional
traditional
SQL
sql
database,
database
you
you
store
store
data
data
as
as
collections
collections
of
of
documents.
documents
Collections
collections
don't
don't
store
store
any
any
data
data
of
of
their
their
own.
own
They're
they're
just
just
containers
containers
to
to
hold
hold
multiple
multiple
documents
documents
that
that
you
you
can
can
query.
query
And
and
a
a
document
document
itself
itself
can
can
just
just
be
be
thought
thought
of
of
as
as
a
a
JSON
json
object.
object
In
in
order
order
to
to
keep
keep
your
your
queries
queries
efficient,
efficient
it's
it's
important
important
that
that
you
you
make
make
your
your
collections
collections
large
large
and
and
your
your
documents
documents
small.
small
In
in
fact,
fact
documents
documents
need
need
to
to
be
be
at
at
least
least
1
1
megabyte
megabyte
or
or
smaller.
smaller
But
but
a
a
collection
collection
can
can
hold
hold
millions
millions
of
of
documents
documents
and
and
still
still
be
be
queried
queried
efficiently.
efficiently
Firestore
firestore
gives
gives
you
you
3
3
fundamental
fundamental
ways
ways
to
to
model
model
your
your
data.
data
The
the
first
first
way
way
would
would
be
be
a
a
root
root
collection,
collection
which
which
is
is
just
just
a
a
collection
collection
of
of
documents
documents
in
in
the
the
root
root
of
of
your
your
database.
database
The
the
second
second
option
option
is
is
to
to
embed
embed
data
data
directly
directly
on
on
the
the
document
document
itself.
itself
It
it
behaves
behaves
very
very
similar
similar
to
to
JSON,
json
so
so
we
we
can
can
save
save
objects,
objects
arrays,
arrays
and
and
other
other
data
data
types
types
on
on
the
the
document.
document
It
it
just
just
needs
needs
to
to
be
be
relatively
relatively
small,
small
again,
again
limited
limited
to
to
1
1
megabyte.
megabyte
Then
then
our
our
third
third
option
option
is
is
to
to
nest
nest
a
a
subcollection
subcollection
on
on
a
a
document.
document
This
this
allows
allows
us
us
to
to
scope
scope
collections
collections
to
to
a
a
parent
parent
document,
document
creating
creating
a
a
hierarchy
hierarchy
structure.
structure
Now
now
that
that
we
we
know
know
the
the
fundamentals,
fundamentals
let's
let's
jump
jump
into
into
the
the
Firestore
firestore
console
console
and
and
start
start
modeling
modeling
some
some
data.
data
The
the
first
first
thing
thing
we'll
we'll
do
do
is
is
create
create
a
a
root
root
collection
collection
called
called
users
users
that
that
maintains
maintains
a
a
document
document
for
for
each
each
user
user
in
in
our
our
app.
app
Every
every
document
document
in
in
a
a
collection
collection
needs
needs
to
to
have
have
a
a
unique
unique
ID,
id
but
but
you
you
can
can
create
create
your
your
own
own
ID.
id
And
and
there
there
are
are
some
some
clever
clever
things
things
you
you
can
can
do
do
to
to
manage
manage
relationships
relationships
by
by
doing
doing
that,
that
which
which
we'll
we'll
get
get
into
into
here
here
in
in
a
a
second.
second
Now
now
let's
let's
assume
assume
that
that
every
every
user
user
in
in
our
our
app
app
might
might
be
be
able
able
to
to
obtain
obtain
certain
certain
in
in
rankings
rankings
or
or
achievements
achievements
through
through
activity
activity
that
that
they
they
perform
perform
in
in
our
our
app.
app
In
in
other
other
words,
words
a
a
user
user
has
has
many
many
rankings.
rankings
The
the
easiest
easiest
way
way
to
to
maintain
maintain
this
this
relationship
relationship
is
is
by
by
embedding
embedding
an
an
object
object
on
on
the
the
document
document
itself.
itself
So
so
we'll
we'll
call
call
that
that
rankings
rankings
and
and
select
select
an
an
object.
object
And
and
then
then
for
for
each
each
relationship,
relationship
we
we
set
set
a
a
key
key
and
and
a
a
value.
value
You
you
can
can
make
make
queries
queries
based
based
on
on
these
these
object
object
properties,
properties
so
so
it's
it's
important
important
to
to
use
use
consistent
consistent
naming
naming
across
across
all
all
user
user
documents.
documents
I'm
i'm
just
just
using
using
a
a
number
number
as
as
the
the
value,
value
but
but
you
you
could
could
nest
nest
an
an
additional
additional
object
object
or
or
other
other
data
data
type
type
under
under
each
each
property.
property
That
that
works
works
pretty
pretty
well,
well
but
but
it
it
It
it
doesn't
doesn't
scale
scale
if
if
your
your
anticipated
anticipated
data
data
footprint
footprint
is
is
any
any
greater
greater
than
than
a
a
megabyte.
megabyte
So
so
embedded
embedded
data
data
works
works
best
best
when
when
you
you
have
have
a
a
small
small
simple
simple
Data
data
structure
structure
that
that
doesn't
doesn't
need
need
to
to
scale
scale
up
up
to
to
thousands
thousands
of
of
records.
records
Let's
let's
go
go
ahead
ahead
and
and
assume
assume
our
our
ranking
ranking
data
data
is
is
much
much
more
more
complex
complex
and
and
we'll
we'll
nest
nest
it
it
as
as
a
a
subcollection.
subcollection
We
we
can
can
click
click
add
add
collection,
collection
which
which
we'll
we'll
call
call
rankings,
rankings
then
then
we'll
we'll
quickly
quickly
add
add
a
a
couple
couple
of
of
documents
documents
to
to
the
the
sub
sub
collection.
collection
The
the
nice
nice
thing
thing
here
here
is
is
that
that
we
we
can
can
save
save
as
as
many
many
documents
documents
in
in
Collection
collection
is
is
we
we
need.
need
And
and
it
it
also
also
maintains
maintains
the
the
relationship
relationship
with
with
the
the
parent
parent
document,
document
which
which
happens
happens
to
to
be
be
the
the
user.
user
That's
that's
great
great
for
for
representing
representing
the
the
relationship,
relationship
But
but
you
you
need
need
to
to
ask
ask
yourself,
yourself
are
are
you
you
going
going
to
to
need
need
to
to
query
query
this
this
data
data
across
across
any
any
parent
parent
documents?
documents
In
in
other
other
words,
words
do
do
you
you
need
need
to
to
make
make
a
a
query
query
that
that
goes
goes
something
something
like
like
I
i
need
need
all
all
users
users
that
that
are
are
ranked
ranked
black
black
belt
belt
because
because
currently,
currently
that
that
type
type
of
of
query
query
is
is
not
not
possible
possible
in
in
a
a
subcollection.
subcollection
The
the
data
data
is
is
completely
completely
scoped
scoped
to
to
that
that
user,
user
so
so
you
you
can't
can't
use
use
it
it
to
to
query
query
across
across
multiple
multiple
users.
users
So
so
what
what
we're
we're
going
going
to
to
need
need
to
to
do
do
in
in
that
that
case
case
is
is
denormalize
denormalize
our
our
data
data
and
and
save
save
our
our
rankings
rankings
collection
collection
as
as
a
a
root
root
collection.
collection
When
when
we
we
create
create
a
a
document
document
in
in
this
this
root
root
collection,
collection
we'll
we'll
want
want
to
to
save
save
the
the
user
user
ID
id
of
of
the
the
associated
associated
user
user
on
on
each
each
individual
individual
document.
document
You
you
can
can
embed
embed
a
a
document
document
ID
id
as
as
a
a
string
string
or
or
the
the
actual
actual
full
full
path
path
to
to
a
a
document
document
in
in
Firestore.
firestore
In
in
most
most
cases,
cases
saving
saving
the
the
document
document
ID
id
as
as
a
a
string
string
will
will
be
be
sufficient,
sufficient
but
but
there
there
may
may
be
be
cases
cases
where
where
you
you
have
have
deeply
deeply
nested
nested
complex
complex
data
data
that
that
would
would
benefit
benefit
from
from
having
having
the
the
full
full
document
document
reference.
reference
In
in
either
either
case,
case
the
the
idea
idea
is
is
very
very
similar
similar
to
to
a
a
foreign
foreign
key
key
in
in
a
a
SQL
sql
database.
database
We
we
can't
can't
do
do
join
join
queries
queries
like
like
we
we
do
do
in
in
a
a
SQL
sql
database,
database
But
but
what
what
we
we
can
can
do
do
is
is
reference
reference
a
a
related
related
document
document
and
and
pull
pull
that
that
in
in
as
as
a
a
secondary
secondary
query.
query
With
with
the
the
root
root
collection,
collection
we
we
can
can
also
also
reference
reference
multiple
multiple
user
user
IDs
ids
to
to
create
create
a
a
many
many
to
to
many
many
relationship.
relationship
By
by
adding
adding
a
a
second
second
user
user
ID,
id
what
what
I'm
i'm
doing
doing
here
here
is
is
creating
creating
a
a
relationship
relationship
that
that
says
says
a
a
ranking
ranking
can
can
have
have
many
many
users
users
and
and
users
users
can
can
have
have
many
many
rankings.
rankings
Now
now
let's
let's
try
try
another
another
example.
example
Let's
let's
see
see
if
if
we
we
can
can
model
model
the
the
relationship
relationship
between
between
tweets
tweets
and
and
users
users
in
in
Twitter
twitter
in
in
Firestore.
firestore
So
so
the
the
first
first
thing
thing
I
i
might
might
do
do
is
is
create
create
a
a
sub
sub
collection
collection
of
of
tweets
tweets
under
under
each
each
user
user
document.
document
A
a
tweet
tweet
is
is
owned
owned
by
by
a
a
user,
user
so
so
logically,
logically
you
you
would
would
think
think
that
that
a
a
tweet
tweet
should
should
be
be
nested
nested
under
under
the
the
user.
user
When
when
displaying
displaying
tweets
tweets
for
for
a
a
given
given
user's
user's
feed,
feed
that
that
should
should
be
be
perfectly
perfectly
fine.
fine
But
but
the
the
one
one
caveat
caveat
there
there
is
is
that
that
if
if
we
we
want
want
to
to
display
display
a
a
feed
feed
that
that
goes
goes
across
across
multiple
multiple
users,
users
we're
we're
going
going
to
to
have
have
a
a
problem.
problem
Every
every
query
query
to
to
this
this
tweets
tweets
collection
collection
is
is
going
going
to
to
be
be
scoped
scoped
to
to
that
that
user
user
document.
document
So
so
let's
let's
imagine
imagine
we
we
want
want
all
all
of
of
a
a
user's
user's
tweets
tweets
for
for
a
a
certain
certain
day.
day
We
we
could
could
say
say
where
where
created
created
at
at
is
is
greater
greater
than
than
or
or
equal
equal
to
to
today.
today
That
that
works
works
well
well
in
in
certain
certain
cases.
cases
But
but
in
in
this
this
case,
case
since
since
we
we
want
want
to
to
query
query
all
all
of
of
the
the
tweets
tweets
across
across
all
all
users,
users
we're
we're
going
going
to
to
need
need
to
to
make
make
this
this
a
a
root
root
collection.
collection
That
that
will
will
allow
allow
us
us
to
to
modify
modify
our
our
query
query
to
to
be
be
scoped
scoped
at
at
the
the
root
root
instead
instead
of
of
underneath
underneath
this
this
user
user
document.
document
We
we
could
could
also
also
try
try
to
to
embed
embed
a
a
tweets
tweets
object
object
on
on
the
the
user
user
document
document
directly.
directly
But
but
Since
since
our
our
users
users
can
can
potentially
potentially
have
have
thousands
thousands
of
of
tweets,
tweets
that
that
doesn't
doesn't
really
really
make
make
sense
sense
from
from
a
a
scaling
scaling
standpoint.
standpoint
So
so
we'll
we'll
go
go
ahead
ahead
and
and
move
move
that
that
collection
collection
to
to
the
the
root,
root
And
and
this
this
time,
time
we'll
we'll
save
save
the
the
user
user
ID
id
on
on
each
each
individual
individual
tweet
tweet
document.
document
We
we
lose
lose
the
the
convenient
convenient
relational
relational
structure
structure
of
of
a
a
subcollection,
subcollection
But
but
in
in
return,
return
we
we
get
get
a
a
more
more
flexible
flexible
structure
structure
for
for
compound
compound
queries.
queries
Now
now
we
we
can
can
write
write
code
code
that's
that's
going
going
to
to
return
return
all
all
the
the
tweets
tweets
for
for
multiple
multiple
users,
users
for
for
example,
example
all
all
the
the
tweets
tweets
created
created
today.
today
But
but
we
we
can
can
still
still
scope
scope
the
the
tweets
tweets
to
to
the
the
exact
exact
user
user
by
by
querying
querying
that
that
user
user
ID.
id
So
so
we'll
we'll
say
say
ref
ref
where
where
user
user
ID
id
equals
equals
user
user
1.
1
And
and
we
we
can
can
even
even
chain
chain
these
these
where
where
statements
statements
together
together
to
to
perform
perform
a
a
compound
compound
query.
query
So
so
that
that
gives
gives
us
us
a
a
nice
nice
one
one
to
to
many
many
relationship
relationship
between
between
users
users
and
and
tweets,
tweets
but
but
users
users
can
can
also
also
like
like
other
other
users'
users'
tweets.
tweets
So
so
how
how
might
might
we
we
set
set
that
that
up?
up
That
that
gives
gives
us
us
a
a
many
many
to
to
many
many
relationship
relationship
where
where
a
a
heart
heart
belongs
belongs
to
to
both
both
a
a
user
user
and
and
a
a
tweet.
tweet
And
and
we
we
also
also
want
want
that
that
relationship
relationship
to
to
be
be
unique.
unique
For
for
example,
example
a
a
user
user
shouldn't
shouldn't
be
be
able
able
to
to
heart
heart
the
the
same
same
post
post
multiple
multiple
times.
times
So
so
one
one
easy
easy
way
way
to
to
enforce
enforce
that
that
uniqueness
uniqueness
is
is
to
to
create
create
your
your
own
own
custom
custom
document
document
ID.
id
So
so
in
in
this
this
example,
example
I
i
have
have
a
a
heart
heart
document
document
where
where
the
the
ID
id
is
is
the
the
user
user
ID
id
followed
followed
by
by
an
an
underscore
underscore
followed
followed
by
by
the
the
tweet
tweet
ID.
id
By
by
doing
doing
that,
that
it
it
becomes
becomes
easy
easy
to
to
query
query
a
a
specific
specific
tweet
tweet
to
to
see
see
if
if
a
a
user
user
has
has
already
already
hearted
hearted
that
that
tweet,
tweet
and
and
it
it
also
also
ensures
ensures
that
that
a
a
user
user
can't
can't
heart
heart
a
a
tweet
tweet
more
more
than
than
once.
once
If
if
you
you
have
have
a
a
SQL
sql
background,
background
this
this
technique
technique
is
is
very
very
similar
similar
to
to
using
using
an
an
intermediate
intermediate
table
table
for
for
joins.
joins
If
if
we
we
have
have
a
a
User
user
ID
id
and
and
a
a
tweet
tweet
ID
id
in
in
our
our
front
front
end
end
app,
app
we
we
can
can
simply
simply
use
use
those
those
to
to
request
request
the
the
corresponding
corresponding
document
document
to
to
see
see
if
if
the
the
user
user
has
has
already
already
liked
liked
a
a
tweet.
tweet
Then
then
we
we
could
could
get
get
the
the
total
total
heart
heart
count
count
by
by
querying
querying
the
the
hearts
hearts
collection
collection
with
with
that
that
tweet
tweet
ID,
id
but
but
this
this
does
does
pose
pose
one
one
major
major
problem.
problem
If
if
a
a
tweet
tweet
has
has
been
been
liked
liked
1,000
1,000
or
or
millions
millions
of
of
times,
times
we're
we're
going
going
to
to
have
have
to
to
query
query
that
that
entire
entire
collection
collection
just
just
to
to
get
get
a
a
simple
simple
count.
count
To
to
overcome
overcome
that
that
problem,
problem
what
what
we
we
wanna
wanna
do
do
is
is
Aggregate
aggregate
our
our
data
data
as
as
it's
it's
created.
created
That
that
just
just
means
means
we
we
sum
sum
up
up
the
the
total
total
hearts
hearts
every
every
time
time
a
a
new
new
one
one
is
is
created
created
and
and
save
save
it
it
on
on
the
the
tweet
tweet
document
document
in
in
the
the
background
background
using
using
Firebase
firebase
Cloud
cloud
Functions.
functions
It's
it's
surprisingly
surprisingly
simple
simple
and
and
I
i
show
show
you
you
exactly
exactly
how
how
to
to
do
do
it
it
in
in
episode
episode
63.
63
But
but
the
the
general
general
idea
idea
is
is
that
that
we're
we're
going
going
to
to
need
need
to
to
show
show
the
the
Count
count
on
on
a
a
tweet
tweet
very
very
often.
often
And
and
we
we
don't
don't
wanna
wanna
read
read
that
that
entire
entire
collection
collection
over
over
and
and
over
over
again.
again
So
so
we
we
set
set
up
up
a
a
Cloud
cloud
Function
function
that
that
runs
runs
whenever
whenever
a
a
new
new
heart
heart
document
document
is
is
created,
created
Then
then
we
we
reference
reference
that
that
tweet
tweet
and
and
increment
increment
or
or
decrement
decrement
its
its
total
total
heart
heart
count.
count
That
that
allows
allows
us
us
to
to
perform
perform
a
a
single
single
document
document
read
read
when
when
showing
showing
Count
count
in
in
the
the
front
front
end,
end
which
which
is
is
going
going
to
to
be
be
a
a
lot
lot
faster,
faster
less
less
expensive,
expensive
and
and
it's
it's
the
the
only
only
way
way
you
you
can
can
scale.
scale
I'm
i'm
gonna
gonna
go
go
ahead
ahead
and
and
wrap
wrap
up
up
there.
there
If
if
you're
you're
a
a
pro
pro
member,
member
be
be
looking
looking
out
out
For
for
a
a
more
more
advanced
advanced
data
data
modeling
modeling
video
video
in
in
a
a
couple
couple
days
days
to
to
answer
answer
some
some
of
of
the
the
questions
questions
that
that
you
you
posted
posted
on
on
Slack,
slack
I
i
hope
hope
this
this
video
video
gave
gave
you
you
some
some
basic
basic
ideas
ideas
on
on
how
how
you
you
can
can
model
model
relationships
relationships
in
in
Firestore.
firestore
If
if
it
it
helped
helped
you,
you
please
please
like
like
and
and
subscribe.
subscribe
And
and
if
if
you
you
wanna
wanna
get
get
my
my
advice
advice
for
for
modeling
modeling
your
your
own
own
complex
complex
data
data
structures,
structures
consider
consider
becoming
becoming
a
a
pro
pro
member
member
and
and
drop
drop
me
me
a
a
line
line
on
on
Slack.
slack
You'll
you'll
get
get
a
a
free
free
copy
copy
of
of
my
my
book
book
as
as
well
well
as
as
1
1
on
on
1
1
project
project
support.
support
Thanks
thanks
for
for
watching,
watching
and
and
I'll
i'll
see
see
you
you
soon.
soon
success
The speakers discuss the challenges of creatingational data models in No Guinea databases and the benefits of root collections for each document. They also discuss the use of a root collection for a root collection of documents and the need for a " many to many" relationship between users and tweets. They explain how to maintain a root collection and use a number of user IDs to create a " many to many" relationship, while emphasizing the need for a more efficient and cost-effective way to model relationships in Firwork. Viewers are encouraged to subscribe and provide more information on models and data structures.
Model Relational Data in Firestore NoSQL
Modeling relational data in a NoSQL database like Firestore can be a challenge, especially if you come from a SQL background because you'll have to unlearn many of the principles that you already know. My goal today is to show you a handful of techniques that you can use to model data relationships, such as one to 1, one to many, and many to many. Because NoSQL is schema less, we have the flexibility to model these relationships in a variety of different ways. I'm going to show you how to build these data models in the Firestore console, and we'll talk about the trade offs for each technique. If you're new here, make sure to like and subscribe and follow along with the source code at angularfirebase.com. Firestore is a NoSQL document oriented database similar to CouchDB and MongoDB if you're Familiar with those. Instead of storing data with a bunch of columns and rows like you would with a traditional SQL database, you store data as collections of documents. Collections don't store any data of their own. They're just containers to hold multiple documents that you can query. And a document itself can just be thought of as a JSON object. In order to keep your queries efficient, it's important that you make your collections large and your documents small. In fact, documents need to be at least 1 megabyte or smaller. But a collection can hold millions of documents and still be queried efficiently. Firestore gives you 3 fundamental ways to model your data. The first way would be a root collection, which is just a collection of documents in the root of your database. The second option is to embed data directly on the document itself. It behaves very similar to JSON, so we can save objects, arrays, and other data types on the document. It just needs to be relatively small, again, limited to 1 megabyte. Then our third option is to nest a subcollection on a document. This allows us to scope collections to a parent document, creating a hierarchy structure. Now that we know the fundamentals, let's jump into the Firestore console and start modeling some data. The first thing we'll do is create a root collection called users that maintains a document for each user in our app. Every document in a collection needs to have a unique ID, but you can create your own ID. And there are some clever things you can do to manage relationships by doing that, which we'll get into here in a second. Now let's assume that every user in our app might be able to obtain certain in rankings or achievements through activity that they perform in our app. In other words, a user has many rankings. The easiest way to maintain this relationship is by embedding an object on the document itself. So we'll call that rankings and select an object. And then for each relationship, we set a key and a value. You can make queries based on these object properties, so it's important to use consistent naming across all user documents. I'm just using a number as the value, but you could nest an additional object or other data type under each property. That works pretty well, but it It doesn't scale if your anticipated data footprint is any greater than a megabyte. So embedded data works best when you have a small simple Data structure that doesn't need to scale up to thousands of records. Let's go ahead and assume our ranking data is much more complex and we'll nest it as a subcollection. We can click add collection, which we'll call rankings, then we'll quickly add a couple of documents to the sub collection. The nice thing here is that we can save as many documents in Collection is we need. And it also maintains the relationship with the parent document, which happens to be the user. That's great for representing the relationship, But you need to ask yourself, are you going to need to query this data across any parent documents? In other words, do you need to make a query that goes something like I need all users that are ranked black belt because currently, that type of query is not possible in a subcollection. The data is completely scoped to that user, so you can't use it to query across multiple users. So what we're going to need to do in that case is denormalize our data and save our rankings collection as a root collection. When we create a document in this root collection, we'll want to save the user ID of the associated user on each individual document. You can embed a document ID as a string or the actual full path to a document in Firestore. In most cases, saving the document ID as a string will be sufficient, but there may be cases where you have deeply nested complex data that would benefit from having the full document reference. In either case, the idea is very similar to a foreign key in a SQL database. We can't do join queries like we do in a SQL database, But what we can do is reference a related document and pull that in as a secondary query. With the root collection, we can also reference multiple user IDs to create a many to many relationship. By adding a second user ID, what I'm doing here is creating a relationship that says a ranking can have many users and users can have many rankings. Now let's try another example. Let's see if we can model the relationship between tweets and users in Twitter in Firestore. So the first thing I might do is create a sub collection of tweets under each user document. A tweet is owned by a user, so logically, you would think that a tweet should be nested under the user. When displaying tweets for a given user's feed, that should be perfectly fine. But the one caveat there is that if we want to display a feed that goes across multiple users, we're going to have a problem. Every query to this tweets collection is going to be scoped to that user document. So let's imagine we want all of a user's tweets for a certain day. We could say where created at is greater than or equal to today. That works well in certain cases. But in this case, since we want to query all of the tweets across all users, we're going to need to make this a root collection. That will allow us to modify our query to be scoped at the root instead of underneath this user document. We could also try to embed a tweets object on the user document directly. But Since our users can potentially have thousands of tweets, that doesn't really make sense from a scaling standpoint. So we'll go ahead and move that collection to the root, And this time, we'll save the user ID on each individual tweet document. We lose the convenient relational structure of a subcollection, But in return, we get a more flexible structure for compound queries. Now we can write code that's going to return all the tweets for multiple users, for example, all the tweets created today. But we can still scope the tweets to the exact user by querying that user ID. So we'll say ref where user ID equals user 1. And we can even chain these where statements together to perform a compound query. So that gives us a nice one to many relationship between users and tweets, but users can also like other users' tweets. So how might we set that up? That gives us a many to many relationship where a heart belongs to both a user and a tweet. And we also want that relationship to be unique. For example, a user shouldn't be able to heart the same post multiple times. So one easy way to enforce that uniqueness is to create your own custom document ID. So in this example, I have a heart document where the ID is the user ID followed by an underscore followed by the tweet ID. By doing that, it becomes easy to query a specific tweet to see if a user has already hearted that tweet, and it also ensures that a user can't heart a tweet more than once. If you have a SQL background, this technique is very similar to using an intermediate table for joins. If we have a User ID and a tweet ID in our front end app, we can simply use those to request the corresponding document to see if the user has already liked a tweet. Then we could get the total heart count by querying the hearts collection with that tweet ID, but this does pose one major problem. If a tweet has been liked 1,000 or millions of times, we're going to have to query that entire collection just to get a simple count. To overcome that problem, what we wanna do is Aggregate our data as it's created. That just means we sum up the total hearts every time a new one is created and save it on the tweet document in the background using Firebase Cloud Functions. It's surprisingly simple and I show you exactly how to do it in episode 63. But the general idea is that we're going to need to show the Count on a tweet very often. And we don't wanna read that entire collection over and over again. So we set up a Cloud Function that runs whenever a new heart document is created, Then we reference that tweet and increment or decrement its total heart count. That allows us to perform a single document read when showing Count in the front end, which is going to be a lot faster, less expensive, and it's the only way you can scale. I'm gonna go ahead and wrap up there. If you're a pro member, be looking out For a more advanced data modeling video in a couple days to answer some of the questions that you posted on Slack, I hope this video gave you some basic ideas on how you can model relationships in Firestore. If it helped you, please like and subscribe. And if you wanna get my advice for modeling your own complex data structures, consider becoming a pro member and drop me a line on Slack. You'll get a free copy of my book as well as 1 on 1 project support. Thanks for watching, and I'll see you soon.
The speakers discuss the challenges of creatingational data models in No Guinea databases and the benefits of root collections for each document. They also discuss the use of a root collection for a root collection of documents and the need for a " many to many" relationship between users and tweets. They explain how to maintain a root collection and use a number of user IDs to create a " many to many" relationship, while emphasizing the need for a more efficient and cost-effective way to model relationships in Firwork. Viewers are encouraged to subscribe and provide more information on models and data structures.