Narrator: In this video, we're gonna use
a simple trick in order to prevent users
from writing multiple reviews for the same tours,
so basically preventing duplicate reviews.
So in the last video, we created all these reviews
in order to calculate the averages.
And we created multiple reviews for one tour,
but all from the same user.
But in practice, that doesn't make much sense, right?
So in the real world, each user
should only review each tour once.
So basically, a duplicate review happens
when there is a review with the same user
and the same tour ID.
And that's what we want to avoid from happening.
And the obvious solution here is
to just use a unique index, right?
However, it is not enough to set
both these fields to unique.
And actually that would really be very wrong,
because that would mean that each tour can get only review,
and each user can only write one review.
And obviously that's not what we want.
So what we do need is them both together to be unique,
so the combination of user and tour to be always unique.
So that sounds a bit complicated, but luckily for us,
that's actually very easy to achieve with indexes.
So we already created a compound index on the tour before,
and so now let's do the same here on the reviews.
So again, right here after the schema definition,
and of course we're still in the review model, right?
So review, and that's not correct,
so review schema dot index...
Tour set to one, and once more it's not really important
if it's one or minus one in this case.
And user also set to one, okay.
And so again, that's similar to what we did before,
but here we're going to take it to the next level
and now add an object for options.
And the option here that we're gonna set is unique
(typing)
set to true.
And that's actually it.
This will achieve exactly what we want.
So now each combination of tour and user
has always to be unique.
So let's give it a save here, or two,
and then go test this out actually.
Now when I first tested this,
it actually didn't work right away.
Really, it only just started working the next day,
and I'm not really sure why, but if that happens to you,
then don't worry about that, okay.
And maybe it might even happen now
as I'm recording this video.
So anyway, right now, there should not be any reviews
on the tour that we were working on.
So just to make sure we still have zero ratings
and also the reviews are empty.
And so let's now once more create
a new review on this tour.
Okay.
So this is one review, so one user and one review.
So that of course should work, but if I now send this again
and let's say I set it to four and say okay,
then this should not be allowed to work, okay.
Just keep in mind that it might still work
because of the issue I was telling you before, okay.
So sometimes this kind of index doesn't get set immediately.
Oh, but actually, in my case here it worked right away.
So we get this duplicate key error on this index here,
and so that's exactly the index that we just created before.
So let's take a look at that also here in Compass.
So here it actually is not,
and so I will do it just like I did before
and reconnect basically.
So going to the recents, then connecting here.
Natours...
Then...
Of course the tours,
and our indexes here are now these four.
And so that's the one that we just created,
or actually it's not (laughs), okay.
So for some reason, it's not here,
so that's just what I was saying earlier,
but don't mind because actually it is working in our code.
Okay, so we were not able to create two reviews
coming from the same user, okay.
But if you now log in as someone else,
well, then we will certainly be able to post this review.
So let's see what user we could use now.
So let's get all users here,
which I can't because I'm not an admin (laughs), okay.
So I forgot about that part,
and so I'm simply going to retrieve this from Compass.
Sometimes that's just easier.
So this is a guide, a user (exclaims),
so let's just use this Eduardo here or something.
So let's log in as this person, as this user.
Yep, with success, and now we should be allowed
to create a review, and indeed here it is, okay.
And if we now update this,
then we have our two ratings, great.
Now just testing it again, so if we send it again,
it's not going to work because again,
we have a duplicate error.
Awesome.
There is just one more thing that I wanted to show you,
and so for that, I'm going to log in as yet another person,
okay, so that I can then create another review.
So this time, I'm gonna be this Max Smith.
And so let's log in.
(mouse clicks)
Okay, so now I'm officially Max,
and I will be able to post this review.
So with five,
I'm able to do that,
and now I want to take a look at the average.
Okay, so right now it is this 4.6666,
which of course does not look good.
And we could fix this in the front end.
For example when we request this data
from the API and then display it,
we could on the front end then do this rounding.
But actually, I want to do it right here on the back end
so that the end user already gets this final rounded value,
which in this case would be 4.7, right?
And for doing that, I'm gonna show you a small new feature
in Mongoose that we didn't use yet.
So let's go to our tour model,
and to that average,
so right here.
And so now, we can use a setter function.
(typing)
So set and this function will be run each time
that a new value is set for this field, okay.
And so here, we usually specify a callback function,
which receives the current value.
And in this case, it returns basically this value,
but rounded.
So we use math dot round for the current value,
but the problem with math dot round is that
it rounds values to integers.
So for example, if we had something like this,
it would then round this to five, okay.
And that's not what we want.
We want it to be rounded to 4.7.
And so we're gonna use a trick here,
which is quite common, so multiplying this by 10,
and so with that, we would get 46.666,
and then this rounded would be 47.
And so then, we divide the results by 10 again,
and that then is 4.7.
All right, and that's actually it.
So again, a setter function here which is going
to be run each time that there is a new value
for the ratings average field.
So just to test this, let's actually simply
update this last review that we added,
which had a five, and let's keep it at five.
So all we basically want to do is to re-run
that function which creates this average.
So if I do this now and now get my tour,
we should get 4.7 here as the average.
And yep, here we go.
Great, and that's actually it already for this video.
So we created this nice setter function,
and we created this unique compound index here
in order to ensure that one user
cannot write multiple reviews for the same tour.