Speaker: Let's now keep working on our API.
Over the next videos, we will implement
a couple of common API features
that make an API easier and more pleasant to use
for whomever is gonna use it.
And in this lecture,
we're gonna start with filtering.
So again, the first feature that we're gonna implement
is to allow the user to basically filter data
using a query string.
Let me just tell you what a query string is using Postman.
So, let's close all these guys.
Alright.
Actually where filtering is gonna make sense
is here on this get all tours route.
Because, this is the one that,
well as the name says, gets all the tours.
And so here, we want to allow the user to filter the data,
so that instead of getting all the data,
he only gets the data that matches the filter.
Like this, we can basically allow the user
to query the data in a very easy way.
Again, using the query string.
And the query string looks a bit like this.
And I'm sure you've seen it.
So we start with a question mark,
and then we can simply specify some field value pairs.
So let's say we wanted to filter
for a duration equal to five and
at the same time, a difficulty of easy.
So we can very simply build a query string like this.
So again, it starts with this question mark,
and then we can specify as many
key value pairs using this format.
And now our Postman application here
actually recognizes this and basically
parses this string here and puts the key and
the values down here in this Params tab.
So we could also specify the key and values here.
So lets say test, set it to twenty three, and
so you see up here that it actually
added it to the query string.
So that's a very nice way of Postman helping us here.
Because now we can, for example, while we're
doing testing we can turn these on and off
and you see that the query string
in here reacts accordingly.
Anyway, we now need a way of accessing this data
that's in a query string, in our Express application.
But, luckily for us, that's very easy,
because Express already took care of that.
So that's just one of the many many things
that Express does for us in order to really make
NodeJS development a lot faster.
So let me show it to you here, actually in the console.
So this data is on a request, which makes sense,
and then it is in a field called query.
So request dot query should then give us
an object nicely formatted with the data
from the query string.
Give it a save.
And note, how I actually added it to
the get all tours handler function.
So again, we do this kind of filtering
in the route where we want to get all the tours.
Let's test it out now with this exact query string.
Sent the request.
And coming back, here we got the object.
Let's actually make this here bigger
so you can see.
That's not really working.
So sometimes I know some people cannot see
the bottom of the screen.
Let me just do it again here.
And so, here we go.
We have the object, which has the duration set to five
and the difficulty set to easy.
And so you see that Express, very nicely,
parsed that string into this, easy to use, object.
Now one thing that I noticed here is that
we're actually missing the log that we had
easier here in the console.
Let's take a look at why that's happening.
So for some reason, we now have the application
before we actually read the environment variable.
That way, the application does not get access to them.
I thought that I had fixed this before,
but for some reason it's back now.
Give it another save here.
And I will try that again.
Now we get our logging back.
A get request to this URL and then with
this rest of the data here.
Anyway, let's now actually use that data
in order to implement our filtering.
But before we can do that, I need to tell you that
in Mongoose, there are actually two ways of
writing database queries.
The first one is to just use filter object
just like we did in the MongoDB introduction section.
Right? Remember that?
The second way is to use some special Mongoose methods.
Let me just, very quickly, show that to you.
With the filter object, we would do it like this.
We start with the find here, and then in there
we can pass a filter object.
And I already mentioned that before.
Again, it works the exact same way as
with a normal MongoDB query.
We could now say, duration
set it to five.
And then,
difficulty, set it to easy.
That's exactly the query string we had before,
and now I'm hard coding it here just to
show you the two different ways of
writing queries in Mongoose.
Let's test this one out.
You see that now, we only get two results.
Let's give ourselves some space here.
Alright.
We see the two results and indeed
they both have a duration of five,
so here and here, and they both are easy.
Easy and easy.
So our filter worked just fine.
That is the first way of writing a query.
Just the normal way that we're already used to,
but there is another way.
Let me show that to you as well.
Again, we start in the exact same way,
but then the difference is that we start
chaining some special Mongoose method to
basically build the query similar to this one that we have.
So we say,
where
the duration
equals five.
Then also, where
the difficulty,
that needs to be in quotes,
equals
easy.
This query here is gonna be exactly
the same as this one, but again, written using
special Mongoose methods.
And there are of course tons of other methods.
For example, instead of equals, we can have less than,
or equal, or just less than, or you can also
sort the results or limit the number of results,
and really all kinds of stuff.
For sure, we're gonna use some of them
over the next couple of videos.
This might look a bit weird here to you,
but its actually very, very useful in some cases.
Just to test, give it a try again to this one.
And indeed we get our same two results.
Now, let me comment this one here out.
I'm gonna leave it here just as a reference,
because right now, we're gonna use this method here.
And we're gonna use it, because in fact,
we actually already have an object that
looks a lot like this.
And that is request dot query.
If you compare it, this object,
to this object that you see down here,
looks exactly the same as this one.
To implement a very simple filter,
all we have to do in this case is to say, req dot query.
Let's try that one more time.
Get rid of this here.
And indeed we get the exact same two results.
Now let's just try only with difficulty set to easy.
Now we get four results.
All of them of course will have
the difficulty set to easy.
Our filter, our very basic filter,
is now actually working.
Now, the problem with this implementation,
is that its actually way too simple.
That's because, later on, we will have
other query parameters.
For example, sort, for sorting functionality,
or page, for pagination.
We need to make sure that we are
not querying for these in our database.
For example, if we added here,
page, equal to two,
then we would, of course, not get any result.
Let me show that to you.
And indeed we do not get any result,
because there is no document in this collection
where page is set to two.
We only want to use this parameter here,
or this field, page, to implement pagination
and not to actually query in the database.
So what we will have to do is,
to basically exclude these special field names
from our query string before we actually do the filtering.
Let's now go ahead and implement that.
What we will do is, to first, create a
shallow copy of the request dot query object.
Let's call it query object.
Here we need, really, a hard copy.
We can't just do, request dot query,
because then, if you would delete something
from this object, we would also delete it
from the req dot query object.
And that's because in JavaScript,
when we set a variable to another object,
that new variable will basically just be
a reference to that original object.
So we really need a hard copy here.
In JavaScript, there's not really a built-in way
of doing this, but a very nice trick that we can use,
since ES6, is to use, first, the structuring
which we use by using dot dot dot.
And then we can simply create a
new object out of that.
Just like this.
This structuring here, these three dots,
will basically take all the fields out of the object.
Here with the curly braces, well,
we simply create a new object.
So we have a new object that is basically
going to contain all the key value pairs
that were in our req dot query object.
So that's a copy.
Now let's create an array of
all the fields that we want to exclude.
I'm calling that one excluded fields.
And then we want to exclude page,
so that's the one that I just
told you before in Postman.
Then, we also want to exclude
sort,
we want to exclude limit,
and we want to exclude fields.
We will implement all of the functionality
of paging, sorting, limiting, and selecting
only some specific fields, later
over the next couple of videos.
Next, what we need to do is to
basically remove all of these fields
from our query object.
And in order to do that, we will
loop over these fields.
So, excluded fields
for each.
And we're using for each because
we don't want to save a new array.
Then each of these of elements,
we can use the delete operator.
And then from the query object,
we want to delete the field with
the name of element, so the current
element in the array.
Like this, we will first delete the field of page,
then the sort, then the limit, then the fields.
Of course, only in case if its there.
If there is, for example, no limit field,
then, of course, that will not be deleted,
because its not there.
Let's now take a look if this actually works.
I'm gonna log req dot query and
at the same time our query object.
Giving this a save.
Don't worry about our results now.
All I want to take a look at is
our query object, just to make sure
that this logic that we have here actually works.
Let's add a sort here of one and a limit of 10.
These will make more sense later.
For now, I am just adding them,
so that I can later exclude them.
Results don't matter, all that matters,
is that our original query is this one here,
and then our query object, which is
the one that we're going to use,
only has difficulty set to easy.
That's because we excluded, page, sort, and limit.
This now works, so instead of using
req dot query, we're gonna use our query object.
Let's get rid of that and try it again.
Now we should be back to querying for
all the documents that have a difficulty
set to easy and ignoring all of these other fields.
Send that.
And indeed, we are back to our four
results that we had before.
We can also turn on the duration,
and indeed, we will now have two results.
Remember that?
And if we turn these off,
well, then off course its gonna work the same.
Let's just delete this empty line here.
And so, indeed, with our original query string,
things obviously also still worked the same.
We prepared our filter feature so that it
ignores page, sort, and limit fields.
But there is actually something more we need
to do in our code, in order to basically
be able to implement these features in future videos.
Let's go back here to our code.
So let's now learn a little bit
about how queries actually work in Mongoose.
I think I mentioned this before,
but its very important to talk about this again.
This find method here is going to return a query.
All of this here will return a query.
And that is the reason why we can then
chain other methods like this.
Like where, equals, and all these other methods
that I talked to you about before.
Let me actually show that to you in the documentation again.
Here we have the query object,
and again, when we use the find method,
it will return an object which is a query.
So in query dot prototype,
we then have all of these methods.
For example, down here we have where,
which I just mentioned.
We have sort which is the one
that were gonna use later to sort.
We have, let's see, we have less than
or less than equal, we have greater.
We have...
What else did we use?
We used equals.
All of these are part of query dot prototype.
Which, again, refers to objects that
we're creating using the query class.
Again, this is the reason why
we can do this sort of chaining.
Now comes the important part.
As soon as we actually await the
result of the query, so as soon as
we use await here or here, for example.
As soon as we use this await,
the query will then execute and
come back with the documents that
actually match our query.
If we do it like this, such as we have here,
then there is now way of, later,
implementing sorting, or pagination,
or all of these other features.
Instead, what we will have to do,
is to save this part here into a query,
then in the end, as soon as we change all the methods
to the query that we need to, only then
by the end, we can await that query.
For example, we're going to use the sort method.
We're going to use the predict method,
we're going to use the limit method,
and we're going to use, really, a bunch of methods,
and chain them to this query.
That would be impossible to do,
again, if we await the result of
this initial query here, right away.
I hope that made sense.
The way that we will do it
is that we
will have our tours down here.
And this will await the query.
The query is, well, simply the
query without any await.
Just to make it consistent with
this part here,
let's do it like this.
Let's say that,
first, we build the query
and then we execute the query.
That's not correct.
Since we're doing comments here,
let's also say send
response.
Just to make our code a bit more easy to scan.
I hope that all of this made sense,
this turned out to be quite a long video,
but it was important to finish
this part here by explaining you
this logic here, so that in the next videos,
we will then be able to implement
the rest of the features.
Because, again, without having it
like this, it would not work.