Jonas: So in the last lecture,
we learned a theory about data modeling.
And, so, let's now use that theory
in order to actually design the data model
of our Natours application.
And this is for me and for many other developers
actually the most difficult part of building an app.
And, so, I hope that this application will serve
as a good example to you and to give you the knowledge
in order to later design your own data models,
basically completely on your own.
So let's do it now.
And let's start with all the datasets
that we actually need in our application.
So starting with the tours,
and that's of course the most obvious one.
And we already have this one implemented.
Then also we need some users.
And, again, we already have a users collection actually
in our database.
And, so, basically tours and users
are two completely separate datasets.
And, so, we have them normalized.
And of course they're not gonna be embedded.
Next up, we're also gonna have reviews,
and we will also have locations.
Okay?
Because most tours actually have a number
of different locations.
Okay?
And, so, that again is yet another dataset.
And finally, we're also gonna have bookings.
But a little bit more about why that is in a second.
Okay, so, we have all these datasets.
Now let's actually model the relationships
that exist between them.
And I'm gonna start with the relationship
between users and reviews.
And this relationship is clearly a one-to-many relationship
because one user can write multiple reviews,
but one review can only belong to one user.
And the parent in this relationship is clearly the users,
and the child, the reviews
because again it's the parent, so the users in this case,
who can be related to many reviews,
but one review can only be related to one user.
Anyway, I chose to model this relationship
using parent referencing.
And that's because a user can write a lot of reviews
and also because we might actually need to query
only for the reviews on their own.
So the data axis pattern is really important
to take into consideration in this particular relationship.
Now, about the kind of referencing that we're gonna use,
it is parent referencing,
so basically the review keeping a reference of the user.
So keeping an ID, basically.
And that is as you already know
because we do not want to allow a race to grow indefinitely.
And that might be the case
if a user writes tons and tons (laughs) of reviews.
Okay?
Also, it's nice to have the review knowing
who actually wrote it.
Okay?
And, so, having the user ID right on the review
will also us to do just that.
All right.
Next up, let's take a look
at the relationship between tours and reviews.
And this one is actually very similar.
So, again, it's a one-to-many relationship,
where one tour can have multiple reviews
but one review can only be about one tour.
Right?
So that's the way it makes sense.
And, so, we're actually gonna model it in the exact same way
as the user-reviews relationship.
So, again, parent referencing,
so that in the end the reviews end up
with a tour ID and a user ID.
And, so, then once we query for reviews,
we always know exactly.
Great, so let's now talk about the relationship
between tours and locations.
So as I mentioned earlier,
each tour is gonna have a couple of locations.
So for example, the park camper will basically stop
in like three or four national parks.
And, so, each of these national parks
is gonna be one location.
Right?
And, so, each tour will basically have a few locations.
Now, following that example, one of these national parks
might also be part of one of the other tours.
And, so, basically this relationship here
is a few-to-few relationship.
And we called this relationship many-to-many before
but we still can also call them few-to-few
or a ton to a ton.
And, so, I called them few-to-few
because each tour is only gonna have three, four locations
but not really like 100.
Okay?
And, again, each of the locations can also be part
of another tour.
Now, this could be a good example for actually implementing
two-way referencing, so basically normalizing the locations
into its own dataset.
Right?
But instead I'm actually gonna denormalize the locations
so to embed them into the tours.
And that's actually for multiple reasons.
First, because there only so few locations.
Also, we will not really gonna access the locations
on their own.
And, finally, these locations are intrinsically related
to the tours because really without locations
there couldn't be any tours.
Right?
So these datasets belong closely together.
And, so, I chose to embed locations into tours
and not create yet another collection for these.
Right?
So we will have one collection for tours, one for users,
and a bit later we will also create a new collection
for the reviews.
All right?
But for locations, again, because these will be embedded
into the tours.
Okay, and next up there's also a relationship
between the tours and the users.
And that's because we're gonna have tour guides
in the tours, and these tour guides will actually be users.
So remember how we actually gave users a role
in our Mongoose schema?
And the possibilities there contained the guide
and lead guide, remember?
And, so, there's gonna be a relationship
between these types of users and the tours.
Now, this relationship is again a few-to-few relationship
because one tour can have only a few users,
so a few tour guides, but at the same time,
each tour guide can also be guiding a few tours.
All right?
And, so, again, there's a many-to-many relationship here,
which I simply called here few-to-few.
Now, about actually modeling this relationship,
we could do it in two ways.
We could use referencing or embedding.
And actually I'm gonna show you how to implement
both child referencing embedding using Mongoose
throughout this section.
Okay?
And the argument for embedding is that in this case
we could then have all the information about each tour
containing the information about tour guides
right on each tour document.
But on the other hand, that would then create
some extra information in the database
because we will still need to have the users
as a separate collection
simply because we need to access them all the time
for user authentication and authorization
and all that stuff.
So usually, users are always an entity on their own
in each database.
Okay?
But we could still embed some of the users into the tours.
So basically when the user is a tour guide
for a specific tour, we could then copy all this data
into the tour document.
Okay?
But also we would then have to update the user on the tour
each time that the underlying user itself changes.
So let's say that the role of a user changes
from guide to a lead guide.
And in that case, we would then have to go to the tour
and also update that role information
right there on the embedded data.
Okay?
And, so, that's not ideal, and so we're actually
also gonna then implement child referencing.
And, so, with that, we can still keep
basically the information about the tour guides
on the users but simply in a referenced form,
so basically keeping the IDs there,
which are then gonna point to the users.
Okay?
And of course we could also use two-way referencing,
so also keeping an ID of the tour right on the user.
But I think that's a bit too much
for this kind of small example
because not all users will actually need an ID of the tour
because not all users are tour guides.
And, so, this relationship here is a bit tricky to model,
I think, but I believe that in the end
child referencing is gonna be the best way to go.
Okay?
But still, I'm gonna also show you embedding
because I think that's also important to learn.
All right?
Next up, we have our bookings.
And basically a new booking will be created
each time that a user purchases a tour.
So this is still kind of a relationship
between users and tours
because again it's a user who is gonna buy a tour.
But we also want to store some data
about that relationship itself,
so in this case about the purchase itself in our database.
For example, the price or the date
when the purchase happened or something like that.
And, so, in cases like this,
it's a good idea to create an extra dataset,
which in this case is the bookings.
Okay?
And, so, of course there will be a relationship
between tours and bookings and also users and bookings.
And, again, because basically the booking connects tours
with users but kind of with an intermediate step.
So one tour can have many bookings,
but one booking can only belong to one tour.
And the same thing with users.
So one user can book many tours,
but one booking can only belong to one of the users.
And, so, of course we have a one-to-many relationship
in both cases, and also in both cases,
we're gonna use parent referencing.
And, so, that means that on each booking
we're gonna keep an ID of both the tour that was purchased
and also of the user who actually purchased the tour.
Okay?
And, so, in this case, I'm doing it this way
because basically I don't want to pollute the tour data
with information about who actually bought the tour.
Right?
It wouldn't be really relevant to the tour data itself.
And the same thing with users.
So we also don't want to pollute the users object
with all of the bookings that they did.
All right?
And, so, instead again
we're gonna create an intermediate object
or an intermediate dataset that's going to stand
between users and tours whenever they create a new purchase.
Right?
Make sense?
And that's actually it for our data model.
And of course, this now looks kind of abstract,
but once we start implementing it,
it's gonna be very helpful
to have all of our ideas organized into something like this.
So whenever this data model that we're gonna implement
throughout this section is becoming a bit confusing to you,
then just reference back to this slide.
Or you can maybe even print it out
if that makes it easier for you.
So this is our data model in theory.
And now throughout the rest of the course,
I will give you the tools to actually model the data
using the Mongoose Library.