-: Welcome back to the next video.
And let's keep it going now with a new
and pretty advanced Mongoose feature
called 'Virtual Populate.'
So, at the point, we have populated
the reviews with the tour and
the user data right here, right?
And so, right now, when we query for reviews,
we get access to that information.
However, that still leaves one problem unsolved.
So, how are we going to access reviews on the tours?
So basically, the other way around.
So, let's say that I queried for a specific tour.
And then, how will I get access
to all the reviews for that tour?
And this problem arises here because
we did parent referencing on the reviews.
So basically, having the reviews pointing to the tours
and not the tours pointing to the reviews.
And so, as we said in the beginning of the section,
in this case, the parent does not
really know about its children.
And so, in this example,
the tour does not know about its reviews.
And sometimes, that's okay.
But in this case, we actually want the tour
to basically know about all the reviews that it's got.
Now, in order to solve this,
with what we know at this point,
we could have two solutions.
And the first one would be to manually query
for reviews each time that we query for tours.
But it would be a bit cumbersome
doing it manually like this.
And the second solution could be to also
do child referencing on the tours.
So basically, keep an array of all
the review ID's on each tour document.
Then, all we would have to do
is to populate that array, right?
But, we actually already ruled out doing this
right in the beginning because
we do not want to store that array
of review ID's that could then grow
indefinitely in our database, right?
And that's actually exactly why we picked
parent referencing in the first place.
However, there is great solution for this.
And that's because Mongoose actually offers us
a very nice solution for this problem
with a pretty advanced feature called 'Virtual Populate.'
So with 'Virtual Populate,' we can actually
populate the tour with reviews.
So, in other words, we can get access
to all the reviews for a certain tour,
but without keeping this array of ID's on the tour.
So, think of 'Virtual Populate' like a way
of keeping that array of review ID's on a tour,
but without actually persisting it to the database.
And so that then solves the problem that we have
with child referencing, right?
So, it's a bit like virtual fields, but with populate, okay?
So, just to illustrate that,
let's go to our tour model here.
And so, what I just described would be,
in theory, to do something like this.
So, on our tour model,
we would add a field called 'Reviews.'
(keys clicking)
And then set the type to Mongoose.Schema.ObjectId.
(keys clicking)
And you don't need to write this.
I'm just doing it to show it to you.
(keys clicking)
And then connect it with 'Review.'
And of course, this here would have to be an object.
But, what matters here is that this is basically
how we would implement child referencing.
So the tour referencing reviews, okay?
But again, we do not want to do it.
So instead, we're going to implement 'Virtual Populate.'
(keys clicking)
All right. And it works like this.
So we do it here on the tour schema.
(keys clicking)
And we still do it .virtual.
So just like with this virtual field here, duration week,
then we type in the name of the virtual field.
So let's call it 'Reviews,'
and then an object of some options.
And the first one is the name of the model
that we want to reference.
And so that works just like with the normal referencing.
(keys clicking)
So again, the name of the model.
So, 'Review' or course, in this case.
And now, we actually need to specify the name of the fields
in order to connect the two data sets.
And this is, for me, the most complicated part
of implementing this 'Virtual Populate.'
And so, let's make sure you really understand this part.
So here, we need to specify two fields.
The foreign field and the local field.
So let us start with the foreign field.
And so, this is the name of the field in the other model.
So in the Review model in this case,
where the reference to the current model is stored.
And that is, in this case, the Tour field, right?
So, let's take look at that.
And so, again, in our review model,
we have a field called 'Tour.'
And so this is where the ID of the tour is being stored.
And so that's why here, in this foreign field,
we specify that name of that field
in order to connect these two models, okay?
And now we need to do the same for the current model.
So, we need to say where that ID is actually stored
here in this current Tour model.
So, local field.
And that is, the ID.
So, _ID, okay?
And so, again, this _ID, which is how
it's called in the local model,
is called 'Tour' in the foreign model.
So, in the Review model. Okay?
And so again, this is how we connect
these two models together.
Let's us write here that this is
'Virtual Populate,' okay?
And so now, with this setup, we can actually use
Populate just like we did before.
And so what we want to do now is to go ahead
and populate the tour when we only
get one single tour, okay?
So here in Postman, it is in this situation of 'Get Tour.'
Okay? Let's see of this actually exists.
And it does, but it's this weird test tour.
And so, let's actually do it with this 'Forest Hiker' one.
All right.
So, when we get this tour,
we now want to populate the reviews.
And it should already be here as an empty array.
Ah, here it is.
It's not an empty array, but it's set to 'null.'
But the virtual field is actually
already there with the reviews.
But it's null at this point because
we didn't yet populate it, okay?
And again, we only want to populate it
here in the 'Get One Tour,' and not
in the 'Get All Tours' because that
would be a bit too much information to send
down to a client when they get all the tours.
Also, when we're getting all the tours,
that's usually to build an overview page.
And in that case, we usually
do not need access to all the reviews.
We only need that when we are
really displaying just one tour.
Okay? And so, I think it makes sense
to only do this populate on 'Get One Tour.'
So, let's do that populate actually
right in the controller.
So, the tour controller and down here
where we have 'Get Tour.'
And so, that's actually very easy.
Only to do is to call 'Populate' after the other query,
and then simply pass the name
of the field that we want to populate.
And so, as we already know, that is called 'Reviews.'
And, so that should actually be it already.
All right?
So, let's test this now, and take a look at this.
And indeed, we now get our complete review right here.
All right? So that got populated.
And so it means that our Virtual Populate
is actually working.
Now, you might start to see that
this is creating kind of a problem
because this here is basically creating
a chain of populates.
And that's not ideal at all.
So, we have the tour being populated with reviews.
But then the reviews also get populated
with the tour again, and also with the user.
And then also the tour is also
getting populated with guides.
Which in this case, is not happening
because there are no guides.
But if there were, then we would have yet another populate.
And so here we would have a chain of three populates.
And so for performance, that's of course, not ideal at all.
Especially here with the tour.
So we have the tour populated with reviews.
And in the reviews, we again have the data about the tour.
And so that doesn't make much sense at all.
So, it's kind of a mess now.
SO, the solution that I'm going to use here
is to actually turn off populating
the reviews with the tours, okay?
So basically, we do not need this data here on each review.
So in this case, I think that's a good solution.
But of course, again in your case,
it will always depend on how
your application works in your specific case.
But in this app, it's more logical to
really have the reviews available on tours,
and it's not that important having the tour
available on the review, okay?
So, let's turn that populate off basically,
in the Review model.
Okay?
So, let's just comment this part out,
copy it down here,
and so, basically get rid of this first one.
Now all right?
Get ready to save.
Let's try it again.
And so, now we should only see the ID of the tour here.
Okay? And indeed, that's exactly what we get.
Okay? Now with this, of course,
we still do parent referencing, okay?
We still keep a reference to the tours here,
but we simply do not populate it.
And again, because we don't always
need that data right here.
All right. Let's just quickly recap what we did here.
So, we started doing only parent referencing on the review.
But that made it so that on the tours,
we had no access to its corresponding reviews.
And the easiest fix for that would be
to also do child referencing on the tours.
But the problem with that would be
that we do not actually want to keep
an array of all the child documents
on the parent document, right?
Because again, we don't want to allow
arrays to grow indefinitely.
So instead of doing that,
we implemented virtual populates like this.
All right? So, just like this.
And this allows us to basically do the exact same thing.
So, keeping a reference to all the child documents
on the parent document,
but without actually persisting that information
to the database.
And so then, after having this virtual populate set up,
all we needed to do is to basically use
Populate just like we did before with the real references.
And then, finally, we also turned off
one of the populates that we had on the review.
So, this one here, where we populated
the tour ID, because that was creating
an inefficient chain of populates.
And so that's, of course, something that we do not want.
And so, in the end, we ended up with
a result like this, okay?
Great.
So, I hope that made sense to you.
And so with this, we actually wrap up for now
of this part of populating, okay?
And so now let's move on to a next topic,
which is basically creating nested routes.