‫In this lecture, we're gonna learn
‫about geospatial queries in order to implement
‫a really cool feature, which is to provide
‫a search functionality for tours within a certain
‫distance of a specified point.
‫So let's say you live in a certain point
‫and wanted to know which tours start at a certain
‫distance from you, like 250 miles, because you don't want
‫to drive further than that in order to start your
‫tour experience.
‫So that would be an awesome feature,
‫and a really nice use case of geospatial queries.
‫And in order to implement something like this,
‫here in our tour router, we could create a nice route,
‫something like this.
‫So I'm going to put it here before this general routes
‫here.
‫So let's say router dot route, and I'm going to call
‫this one tours within, okay?
‫Then we also need to specify the distance.
‫And so therefore we create a distance parameter here.
‫Next, we also need to specify the center,
‫and that's basically the point where you live.
‫So let's specify it around center and then slash,
‫and then lat and longitude.
‫So basically into this variable here,
‫you want to pass in the coordinates of the place
‫where you are.
‫So let's say you live in Los Angeles
‫and wanted to find all the tours within a distance
‫of 300 miles.
‫So here you would say 300,
‫and then here you would put the coordinates
‫of basically where you live, all right?
‫Then, let's also provide the option of specifying
‫the unit.
‫So if this distance here is in kilometers or in miles.
‫So let's say, slash unit,
‫and the unit as the parameter.
‫Now this way of specifying a URL is something
‫that we never did before.
‫So basically saying here center and slash
‫and putting the longitude and latitude after that,
‫and then slash unit,
‫and then after that the queries parameter.
‫And of course we could also make it so that
‫user should specify all of these options
‫using a query string, but this way it looks way cleaner
‫and it's also kind of a standard way of specifying URL's
‫which contain a lot of objects.
‫So what I was saying that instead we could do it
‫of course like this.
‫Tours distance and then the user would have to provide
‫the distance like this here.
‫And the center like this, let's say minus 40,
‫and 45, and then the unit also sets to miles.
‫Okay, so this has been one way using query strings
‫and we have done it before.
‫And actually not like this, but like this.
‫Okay, but instead we are going to specify
‫it again like this.
‫Which in my opinion looks way nicer.
‫So minus 40, 45.
‫Okay, so think that this here really looks a lot
‫cleaner than this.
‫Okay, and so this is another thing that I wanted
‫to show you is that it's kind of a standard of
‫specifying URL's like this, all right?
‫Anyways, for this route of course,
‫we need a route handler, and that's gonna be at
‫tour controller, and let's call this handler function
‫get tours within.
‫And so let's now go ahead and implement this method
‫to this handler.
‫So tour controller and right down here.
‫And let's start by getting all parameters.
‫So let me just copy all of this here.
‫And just so we know what our data looks like.
‫All right?
‫So this, and then this has a comment.
‫All right, so let's use a simple restructuring
‫to get all our data at once from the parameters.
‫And actually we need to say const,
‫and distance, and unit.
‫So all of that comes from request dot params.
‫Okay, so on req.params, we have dot distance,
‫dot center, and dot unit, right?
‫Because these are the names of the three parameters
‫that we specified here.
‫And actually it's not center,
‫it's latlng here, okay, so that's the correct one,
‫and then finally the unit.
‫So again, we use restructuring to get all these
‫variables at once.
‫Next up, let's actually get all coordinates from this
‫latitude longitude variable here.
‫So as we see, we expect the data in the format like this,
‫and so that makes it really easy to copy this data
‫from Google Maps.
‫So let me just quickly show that to you.
‫It's important that you understand how this kind of stuff
‫also works in practice.
‫So let's use the Los Angeles example here.
‫Okay, and so let's say you live somewhere here.
‫Okay, and so you can very easily get the latitude
‫and the longitude like this, okay?
‫So put that here, just as an example again.
‫And so this is the format.
‫It looks like latitude and longitude.
‫And so let's now create one variable for each of them.
‫So one for latitude and one for longitude.
‫So lat longitude dot split because it's a string
‫and we want to split it by comma.
‫And so that will then create an array of two elements,
‫and now we can again use destructering in order to save
‫these to the two variables that we're interested in.
‫So latitude and longitude.
‫Okay, next up, I want to test if we actually have
‫the longitude and latitude variables to find.
‫Because if not, then it means the user didn't specify
‫them in the required format.
‫So let's say if there is no latitude,
‫or no longitude, then we want to create a new error.
‫So as always, new app error, and I'm not sure
‫if we have it actually still, right here,
‫and actually we don't.
‫So we commented out this part because we no longer
‫needed the app error after creating our handler
‫factory functions, but now, we actually need it again,
‫and so let's bring it back.
‫And the message here is gonna be please provide,
‫in the format, like this.
‫And error code 400 for that request.
‫Great, and now just to see if all of this is
‫working correctly,
‫let's just log out of this to the console.
‫The distance that longitude and unit.
‫And let's also send a generic message here,
‫just to really finish the request to respond cycle.
‫And for that of course is the same as always.
‫So status success for now, and the of course later on
‫we will send the data that the query returns.
‫So actually let's copy this here.
‫Because this kind of is already what we want.
‫So back in Postman, let's close all of this.
‫This one we no longer need.
‫All right, we also don't need to be logged in,
‫and we also don't want any updates.
‫So anyway, we also need to get this part
‫which I'm too lazy to write out.
‫And so let's test that now.
‫And that didn't work,
‫and I think this one we called tours within actually.
‫Did we?
‫So tours within.
‫So I'm not sure why I wrote distance here
‫let's just quickly fix that here in our examples.
‫And so of course it's tours within.
‫And that's still not working, so let's take a look
‫and route implementation here actually.
‫So here we have that, and yes of course,
‫this is not correct at all.
‫So this one here should not be inside dot route, obviously.
‫But instead, it should be in the get method.
‫And so now that looks a lot more like what
‫we have here.
‫So dot route and then dot get.
‫So one less try and this time it worked.
‫And let's take a look at the console,
‫and indeed, here we get distance that is specified
‫the latitude to longitude, and the unit.
‫Great, and so now it's time to actually write
‫the query itself.
‫Now a geospatial query actually works quite similar
‫to a regular query.
‫So we're still going to write tours is equal to tour.find,
‫and of course, awaiting the result of this promise.
‫And then the old game of marking everything
‫as async and then catch the async and wrap our function
‫in there.
‫So we have all tours here unused,
‫and so let's go ahead and edit to the response object
‫right away.
‫So data, and now remember how we called it data.
‫And then tours.
‫Okay, and all we need to do is to specify
‫or filter object here.
‫So remember that we want to basically
‫query for start location,
‫because the start location field is what holds
‫the geospatial point where each tour starts.
‫And so that's exactly what we're searching for.
‫So, start location, and now we need to specify
‫the value that we're searching for.
‫And for that, we will now use a geospatial operator
‫called geo within.
‫As always, we need to specify the subject,
‫and then here, where we would earlier use like some
‫math operator like greater than,
‫this time we use a geospatial operator like this one.
‫Geo within, and this operator does exactly what it says.
‫Basically it finds documents within a certain geometry.
‫And that geometry is what we need to define as a next step.
‫So we want to find documents,
‫but where do we actually want to find these documents?
‫Well we want to find them inside of a sphere that starts
‫at this point that we defined,
‫and which has a radius of the distance that we defined.
‫So again with our example in Los Angeles,
‫if you specify the distance of 250 miles,
‫then that means you want to find all the tour documents
‫within a sphere that has a radius of 250 miles.
‫Okay, make sense?
‫And so now we need to pass the information here
‫into the geo within operator, okay?
‫And we do that by defining a center sphere.
‫Okay, and again, I know that this looks quite confusing,
‫but that's why I'm explaining it here step by step.
‫And also in a second, we're going to take a look
‫at the documentation.
‫So the center sphere operator takes an array
‫of the coordinates and of the radius.
‫And let's actually format the code here to at least
‫make it look a bit easier, okay?
‫Well it kind of looks the same, but anyway,
‫that's how you find the coordinates here.
‫And for that, we need yet another array,
‫and then the longitude and the latitude.
‫And that's right.
‫You first need to always define the longitude
‫and then the latitude, which is a bit counterintuitive
‫because usually coordinate pairs are always specified
‫with the latitude first, and the longitude first.
‫I think I mentioned it before that in geo adjacent,
‫it for some reason works like this.
‫So that is the center of the sphere.
‫Now we need to specify it's radius.
‫Now here we actually do not pass in the distance,
‫but instead it expects a radius in a special unit
‫called radians.
‫So let me put radius variable here,
‫and then in a second we are going to define it.
‫So let's now actually define the radius.
‫So again, the radius is basically the distance
‫that we want to have as the radius,
‫but converted to a special unit called radians.
‫And in order to get the radians,
‫we need to divide our distance by the radius
‫of the earth.
‫So that sounds a bit crazy but really this is how it works.
‫Okay, so now we actually need to take into consideration
‫our units here, because of course the radius of the earth
‫is different in miles then in kilometers.
‫So let's now do a turnery of greater here
‫and say that if the unit is equal to miles,
‫well then the result here should be distance.
‫So basically our original radius divided by 3963.2.
‫Okay, so again, that is the radius of the Earth in miles.
‫Okay, and otherwise,
‫we will then assume that it's kilometer.
‫And so if it is kilometers, then it is the distance
‫divided by 6,378.1 kilometers.
‫All right, so again, this kind of crazy conversion here
‫is necessary because normally it would expect
‫the radius of our sphere to be in radians.
‫And radians we get by dividing the distance
‫by the radius of the Earth.
‫Great, so we're almost ready to test this now.
‫Let's just add the results property that we used to have.
‫So with the number of results basically,
‫and then another very important thing
‫is that we actually in order to be able to do just
‫basic queries, we need to first attribute an index
‫to the field where the geospatial data
‫that we're searching for is stored.
‫So in this case, we need to add an index to start location.
‫So let's do that here in tour model.
‫So down here, we need yet another index.
‫Tour schema.index.
‫Start location, but now we're actually not going
‫to set it to one or minus one,
‫because this time it is a different index
‫that we need.
‫So for geospatial data, this index needs to be a
‫2D sphere index if the data describes real points
‫on the Earth like sphere.
‫Or instead, we can also use a 2D index if we're using
‫just fictional points on a simple two dimensional plane.
‫Now in this case of course, we are talking about real
‫points on the Earth's surface,
‫so we're going to use a 2D sphere index here.
‫So a 2D sphere like this.
‫Okay, and so we're basically telling that this start
‫location here should be indexed to a 2D sphere.
‫So an Earthlike sphere where all our data are located.
‫Great, and with that, we should now actually be ready
‫to test out our new route.
‫Okay, let's just increase this radius here a little bit
‫by 400 miles, and so let's see what we get.
‫Well, we get this weird looking error,
‫and I'm not really sure what that is.
‫And so, let's take a look.
‫And probably it's something wrong here with the radius,
‫but actually this is a great time to test out our debugger.
‫So I showed that to you a long time ago,
‫but this is a good use case for actually taking a look
‫at the debugger again.
‫So I think we have a NPM script for that,
‫it's called debug.
‫So we need to know this process, and then say NPM run debug.
‫Okay?
‫So that should open up this nice window.
‫And let's close up all of these files here.
‫Okay, and so we're in our controllers.
‫Tour controller.
‫And so now, let's set a break point right here.
‫Okay, because at this point, we will have all our
‫variables defined, and so we can then take a look
‫at their values in order to see what's going on.
‫So of course we could of done that with a simple
‫console.log, but in some situations where you have
‫a lot of stuff going on,
‫and it's also nice to use the debugger.
‫So we need to send our request.
‫And now we're in the debugger at our break point.
‫And so at this point in time,
‫our code has really stopped.
‫And we can see here that actually all our variables
‫seem to be defined.
‫Let's take a look at that here in local.
‫So we have a distance, we have the latitude,
‫the longitude, and also the radius.
‫So let's keep going here.
‫So we jumped right to then next line because this if
‫here didn't enter the if block.
‫And so now it's running all of these functions.
‫But let's step out of them actually,
‫and now we actually get tours undefined.
‫So if we now continue this, we will probably get
‫that same error again, right?
‫And so indeed, we do.
‫So it must be something here in this line, let's say,
‫and indeed, I think I found the error.
‫So that's now how you write sphere.
‫So sphere is more like this, right?
‫So I hope and I believe that actually this was the error.
‫So let's actually close up the debugger here,
‫and let's relieve, and actually that should of
‫updated my code here.
‫Let's see, and it actually didn't.
‫I'm not sure why that is, but all right.
‫Let's run this here again.
‫Not this command, okay anyway,
‫let's just do NPM start.
‫Close all of these.
‫And now try it again.
‫Ah, and now we get some real results here, nice.
‫So it tells me that these tours here are in that
‫400 mile distance that is specified.
‫But how can we really know that it's true?
‫Well actually we can use compass for this.
‫So if we open up compass here, we have something really nice
‫which is this schema.
‫So let's do that here in the tours.
‫And so we can now come to schema here
‫and then analyze this schema.
‫So analyze the schema, and so now here we have a nice
‫summary for all of our fields.
‫For example you see that the difficulty
‫is easy in 50% of the documents,
‫medium in 30%, and difficult in 20%.
‫You also see these durations.
‫So in 20% it's five, and also nine is also more popular
‫than the other durations.
‫Now what I'm really interested in here is the locations.
‫Or actually the start locations, so that's here.
‫And now normally, you would see a map here.
‫But right now, there is no map because we have
‫a document right now which doesn't have a start location.
‫So in order for this to work properly,
‫we need to get rid of that document.
‫So it was one of these testing documents
‫that we created, but which we now actually no longer need.
‫So let me see if it shows up here.
‫Maybe it's in the end.
‫So yeah, it's this new test tour.
‫Let's get rid of that.
‫Back to our schema, let's analyze it again.
‫Okay, so here we have the star applications.
‫And here you already see the map, so that's great, right?
‫That's really really a handy feature.
‫So we should have nine points here.
‫So one, two, three, four, five, six, seven, eight,
‫one up here, the ninth one.
‫So probably this one is to see the Northern Lights.
‫Right?
‫And now here we can actually replicate that query
‫using this graphical interface.
‫So let's come to Los Angeles and approximately
‫to the point where we were.
‫So let's say here, and I'm zooming out a little bit
‫just to give some more space,
‫but now we can hit shift and then drag.
‫So just as it says down here.
‫So put the hand where our point was,
‫so basically somewhere here in Los Angeles.
‫Now shift, and then drag, now all right.
‫And so you can see that as we start increasing
‫this sphere, we get more and more towards
‫some more of these points here turning yellow, okay?
‫And I believed that our radius was something like 0.1,
‫or something like this, let's say.
‫And so you see that actually three documents,
‫so three tours all within this sphere
‫that we just created, okay?
‫So we have this sphere with the radius of 0.1,
‫as you can see up here.
‫And so in fact this query looks just like the one
‫that we just filled with code in our controller function,
‫right?
‫So we get these three documents that are marked
‫as orange, so let's actually now hit analyze here again,
‫and so now our query returned three documents.
‫So let's see the names actually.
‫So where is that?
‫For the sports lover, the wine taster,
‫and the park camper.
‫And the three tours that we got in Postman,
‫so as a result for API, should be these exact same three.
‫So the sports lover, wine taster, and park camper.
‫So park camper, then you have,
‫the sports lover,
‫and the last one remember should be the wine taster,
‫and indeed it is.
‫Great, that's really fantastic.
‫Let's now just decrease this here to 200,
‫let's see how many results you get then,
‫and in fact it's only just one.
‫So let's come back to compass here.
‫Let's delete this query here.
‫So if we get back to all our documents,
‫take a look at our map.
‫And so here is LA, okay.
‫It's another radius should be something like 0.05.
‫Now something like this, let's say,
‫and so indeed we only get this one tour which I believe
‫is the wine taster or something.
‫Yeah, we have to analyze this.
‫So oh, actually it's the sports lover.
‫And so let's see if that's what we got here.
‫And indeed, it is.
‫So it's the sports lover, and so that is the only tour
‫that is within 200 miles of Los Angeles.
‫Fantastic.
‫So this I find really really exciting, really cool.
‫Let me just show you another thing very quickly,
‫so you can also see all of our locations on the map.
‫So that's here.
‫And so these are the locations of all of the tours,
‫so the ones that are stored in location.
‫So we have the coordinates, and we also have days,
‫descriptions, and the time, okay?
‫And of course our query is not about all of this here.
‫For that query we really just care about the
‫star locations, but I still wanted to show you this map
‫where we can actually see all of the locations as well.
‫Okay, so there really is a huge potential for using
‫geospatial data with MongoDB.
‫The possibilities are really endless for doing
‫geospatial queries like this.
‫And I actually wanted to show you if we have some more
‫geospatial operators in MongoDB.
‫So let's take a look at the documentation here again.
‫So let's come down here again to the reference
‫and operators, and query operators.
‫And I think it's somewhere down here, yeah.
‫So here we have the geospatial operator.
‫And you see that this is the one that we used,
‫so the one that selects geometries within a bounding
‫geo adjacent geometry.
‫That sounds a bit weird, but this bounding
‫geo adjacent geometry is that sphere that we defined
‫in our code, right?
‫Now we could also have used the snear here,
‫let's take a look at that.
‫And so that would of looked a bit different.
‫So in this case, we would of defined the point
‫using the longitude and the latitude that we got,
‫and then we would of specified the maximum distance
‫here in meters.
‫And then in this case, we didn't need to do the conversion
‫to radians.
‫Okay, what's also nice about this one
‫is that we can specify a minimum distance.
‫And so with this, we could for example exclude
‫tours that are only 50 miles away from our starting point,
‫okay?
‫So if you someday need something like this,
‫then you can always use the new operator here as well.
‫Okay, great.
‫So with this geospatial query that we just defined here,
‫we basically found documents that are located
‫within a certain distance of our starting point.
‫But what if we actually wanted to know the exact
‫distances of all the tours to that starting point?
‫Well that's exactly what we're going to calculate
‫in the next lecture.