Welcome back after a long lecture
where we searched for tour documents
within a certain distance
from a certain point using geospatial queries.
Now in this lecture, let's use geospatial aggregation
in order to calculate distances
to all the tours from a certain point.
So just like before let's actually start
by defining the route so that we know
which data we're going to be working with.
So router.route;
at this time I'm gonna call it simply distances,
and then the data that we need
is the latitude and the longitude
of the point where the user currently is,
so in our previous example that was LA,
and then let's also allow the user
again to specify the unit.
Then here, the route handler function.
Now this time here,
we do not need the distance parameter,
as we had it right here,
because we're not gonna be searching for a certain radius.
We're really gonna calculate the distance
from a certain point to all the tours
that we have in our collection.
So the handler is at tourController,
and it's gonna be called getDistances.
We don't have that yet, and so let's create it.
CatchAsync, and then of course
mark the function as async as well
because we already know that we're gonna use
the aggregation pipeline, and so by the time
we're gonna be using a wait.
The beginning of this function is actually quite similar
to the getToursWithin one, and so let's go ahead
and just copy all of this code.
We have some similar units, then we also need to get
the latitude and longitude,
and we also need to create this error
in case there is no latitude or no longitude.
This one here does not apply,
and also we don't have the distance.
So let's now do the actual calculation.
Just like before in order to do calculations
we always use the aggregation pipeline.
And remember, that is called on the model itself.
So Tour.aggregate.
Then let's await that, and save it
into the distances variable.
So then here, remember, we passed in an array
with all the stages of the aggregation pipeline
that we want to define.
Now for geospatial aggregation,
there's actually only one single stage,
and that's called geoNear, so this one.
Again, this is the only geospatial
aggregation pipeline stage that actually exists.
This one always needs to be the first one in the pipeline.
So keep that in mind that geoNear
always needs to be the first stage.
Something else that's also very important to note
about geoNear is that it requires
that at least one of our fields
contains a geospatial index.
Actually we already did that before,
so let's again take a look.
Our start location already has
this 2dsphere geospatial index on it.
Since we're using this startLocation
in order to calculate the distances,
well, that's then perfect.
If there's only one field with a geospatial index
then this geoNear stage here will automatically use
that index in order to perform the calculation.
But if you have multiple fields with geospatial indexes
then you need to use the keys parameter
in order to define the field
that you want to use for calculations.
So keep that in mind, but again,
in this case we only have one field,
and so automatically that startLocation field
is going to be used for doing these calculations.
So, what do we need to pass into geoNear?
Well, first we need to specify the near property,
and near is the point from which to calculate the distances.
So all the distances will be calculated from this point
that we define here, and then all the start locations.
So this near point here is of course
the point that we pass into this function
with this latitude and longitude.
Now we need to specify this point here as geojson,
so that's just like we did it before,
where we need to specify the type as Point,
and then specify the coordinates property.
And as always the first coordinate here is the longitude,
and then the second one, the latitude.
And let's multiply both of them by one,
simply to convert it to numbers.
So this is the first mandatory field, near,
and the second one is the distance field property.
So, distanceField, and so this is the name
of the field that will be created
and where all the calculated distances will be stored.
So let's simply call this one distance.
Actually, that's it.
That's all the fields that are mandatory
in this geoNear stage.
And of course, we can add other stages here,
and we're actually going to do that a bit later,
but for now all I want to do
is to really see the results of this working.
Let's again copy this a result here,
thus sending these results, and here,
let's then send the distances,
and also this one we don't need.
So, we're ready to start.
Keep in mind that at this point we didn't use the unit,
but don't worry about that.
We're gonna do that in a second,
but again, first I really want to see this working.
Remember that the route is now distances,
so let's just copy this one here.
Actually, I will save it also,
so into the tours.
Let's say get tours within radius.
This here is called distances,
and we do not have this and also not this.
So just the coordinates, and then again the unit.
Let's take a look, and we will now get this error.
Remember how we said that geoNear
always needs to be the first stage in a pipeline,
but if you now take a look at the code
you might think that actually our geoNear stage
is currently the first stage of our pipeline.
Because right here, it actually looks like it is, right?
There's nothing before this, and so why do we get this error
that geoNear is not the first stage in the pipeline?
Actually it took me a bit of time to figure this out
because this has something to do with a piece of code
that we wrote a long time ago.
That's here in the tour model,
and if we go down here, I think.
Right here, we have this aggregation middleware,
and remember that what this did is to actually always add
this match stage here before all the other stages,
and actually we have this console.log here
and so indeed you can actually see
the entire pipeline down here.
And so you see that we first have the match,
and then the geoNear phase here,
actually only as the second stage.
So it actually makes sense that we get that error.
Now we could go ahead and change this middleware here
and say that if geoNear is the first operator
in the pipeline, then simply do not do this here.
But that's a bit too much work for this use case,
so all I'm gonna do is to get rid of this middleware.
So give this a save, and now let's try it again.
Now we get our tours, and now it should have
that distance field on them.
So let's search for that, and indeed here it goes.
So distance, and then this huge number here.
It is this big number,
because actually it's calculated in meters,
so this result comes in meters,
so let's first of all convert this one to kilometers.
Later on we will then also convert it to miles,
because remember we specified the unit to miles,
but for now the easiest solution
is to actually convert it to kilometers,
because all we have to do for that
is to just divide it by 1000.
And then also what I want to do
is to only really get the distances,
and the name of the tours.
So get rid of all the other clutter that we have here
and really only focus on the distances themselves.
For that, as you might remember,
we can use the project stage.
So let's add that here as the second stage.
So project, and then basically the names
of the fields that we want to keep.
So that's the distance, and so we set that one to one,
saying that we want to keep it, and then also the name
so that we actually know what tour we're talking about.
With that we get rid of all the other data,
and now let's basically divide the distance by 1000
in order to convert these meters to kilometers.
Actually, it's very easy to do that,
because in a geoNear stage we can actually specify
the distance multiplier property.
So distanceMultiplier, and so here we can specify a number
which is then going to be multiplied with all the distances.
Here we specify 0.001, and so that is exactly the same
as dividing by 1000.
So let's test our result here now.
And that calculation apparently takes some time,
but now here we go.
So now you get this nice result here in kilometers.
As you see the Sports Lover is the closest tour
to the location in Los Angeles that we marked.
So it's only 64 kilometers away,
which should be something like 40 miles.
But again, we're going to do that conversion in a second.
For now, I just want to go back to that map and compass
and see if this actually makes sense.
So we're still here,
and we still have our start locations map.
Now the problem here
is that we actually cannot really click
on any of these points and see what they are.
But let's just draw a quick circle here again
just to see which are the closest tours,
and if they match the ones in our output.
So it's kind of here, I believe,
and so let's include these five tours here.
So their names are The Park Camper, Snow Adventurer,
Wine Taster, Sports Lover and Star Gazer,
and so now when we come here,
these ones are actually the first five ones.
Sports Lover, Park Camper, Wine Taster,
Star Gazer and Snow Adventurer.
So that one that's really close is the Sports Lover,
and then the next one is the Park Camper
and the Wine Taster.
This one here is going to be the Park Camper,
which I believe starts in Las Vegas,
so that makes sense,
and then a third one is here,
The Wine Taster close to San Francisco.
So that distance of 800 kilometers I think,
or what was that?
Yeah, 600 kilometers, that actually makes kind of sense.
So, something close to 400 miles here.
And speaking of miles, let's actually do
that conversion right now.
Let's do something similar to what we did before,
so testing for the unit.
Let's create a multiplier variable,
again a ternary operator here,
so if it's miles then what should our multiplier be?
Well, let's actually very simply
Google what one meter is in miles.
One meter to miles, and Google usually gives us
a pretty nice response, and so indeed, that it is.
So if this is one meter, then all we need to do
is to really multiply our result in meters
with this number.
So let's copy it here, and go back,
and so this should be our multiplier
in case the unit is meters, or actually in case it's miles.
And in case it's meters, well then
it's that 0.001 that we used before.
We don't want it in meters,
because that's not really a readable unit.
Instead we want it in kilometers.
So now we can go ahead and use out multiplier variable here,
give it a save, and let's try it out.
So take a look at what we have here in kilometers,
so from the previous result, which is 64.
That should approximately be 40 miles, so let's send that,
and that was pretty close.
So 40.2 miles indeed.
And so that's our closest tours,
and the most farthest away is the City Wonderer,
which I think starts in New York or something,
and so that is more than 2400 miles away from L.A.
If we then set it here to kilometers,
then it should be back to getting the value
that we had before.
Let's put it back to miles
because I know that most people watching this course
are from the U.S., and so over there they use miles
instead of kilometers.
So let's save this here as well to our collection
get distances to tours from point.
So that's it, that wraps up this lecture,
and that's all I had to show you about geospatial data.
So this video and the last one should have given you
a very great overview of how to work
with geospatial data in MongoDB.
And as I said before there's a ton of possibilities
of stuff that you can do in your own applications
using this kind of data.