Narrator: In this video, we're gonna talk about
the second type of Mongoose middleware,
which is query middleware.
And, as the name says, query middleware
allows us to run functions before or after
a certain query is executed.
And so let's now add a pre-find hook,
so basically, a middleware that is gonna run
before any find query is executed.
So let's add another comment here.
So this is query middleware.
And of course, it works in a very similar way
as before.
So the tourSchema, and then pre,
and in here we define the hook,
which is gonna be "find."
So, function, and again, we have access
to the next function.
And actually, let's start by calling it here
at the end so that we don't forget it.
Okay, and so you see, that it looks
exactly like the other middleware,
the other pre-hook.
The only difference here is really this find hook,
which will make this query middleware
and not document middleware.
All right?
And so, the big difference here is
that the this keyword will now point at the current query
and not at the current document,
because we're not really processing any documents here.
We're really gonna be processing a query.
Okay?
And the use case that we're gonna do here is this.
So let's suppose that we can have
secret tours in our database,
like for tours that are only offered internally,
or for a very small, like, VIP group of people,
and that the public shouldn't know about.
Now, since these tours are secret,
we do not want the secret tours to ever appear
in the result outputs.
Right?
And so what we're gonna do is to create a secret tour field
and then query only for tours that are not secret.
All right?
So, let's do that.
Start by adding here, and let's do it right at the end.
secretTour, so the type should be a Boolean,
and I think this is the first time
we're actually using a Boolean,
so this would either be true or false.
So if it's true, then it is a secret tour,
and we don't want it to show up,
and, by default, it is false.
All right?
And so, basically, usually the tours are not secret.
Okay.
Next up, let's actually go ahead and create a secret tour
so that we can then test using it, okay?
So let's call this here the Super Secret Tour.
All of this doesn't matter,
and let me just say that secretTour is indeed true.
Okay?
Save it here ...
And, uh, where is that?
Yeah. So secretTour is true.
And let's now actually head over to Compass
and delete all of these other tours that we had there.
So, let's reload this ...
And then, down here, we should have ...
Yeah, this test tour here, we can delete that.
Another test tour ...
So all this clutter here, basically,
we want to get rid of it.
We only keep the secretTour here
so that we can now experiment with it.
Okay, and you see here that secretTour is set to true,
while the other ones, of course,
do not have this attribute,
because, by the time we created them,
we didn't have the secretTour yet.
So, now, just before we start,
back in Postman, let's confirm that, indeed,
the last tour here, and actually it's not the last tour,
so where is it?
Oh, it's the first one.
So here we have the Super Secret Tour,
of course, set to true.
All right? So, moving back now,
let's now execute the query
like we just said in the beginning.
So, again, keep in mind that this here
is now a query object, all right?
And so we can chain all of the methods
that we have for queries.
And so that simply adds a find method here,
and then basically select all the documents
where secretTour is not true, okay?
So secretTour ...
And then is not equal to true.
And I'm doing it like this because, remember,
the editors are not currently set to false.
They do simply not have this attribute.
All right?
Okay, so let's test it out now again.
And so it should now be gone here.
And indeed, it is no longer here.
Okay, we have all the tours that are not secret,
but not the secret tour.
And I'm now seeing that we actually have
secretTour set to false here,
but that's really just a Mongoose thing,
so let's take another look here at Compass,
and reload.
And so you see that, actually, in the database,
secretTour is not set to false.
Mongoose is simply adding that because we have it
in our schema as the default, and it's not in the database.
And so it's basically, then, putting it there anyway.
So you see that we actually have 11 documents,
but then here in Postman we only get ten results.
And that is, of course,
because our Secret Tour is basically filtered out.
Okay?
So, let's try to understand again
what really happens.
So as soon as we hit this route, here,
using the get method,
let's see what happens.
So, what happens is that we create a query
using tour.find.
Okay?
And then, of course, we chain all these methods to it
as we talked about in one of the last lectures,
and then, by the end,
we then execute that query here by using await.
Right? Remember that?
So this is where we execute the query.
But, before it actually is executed,
then, our pre-find middleware here is executed.
Okay?
And it is executed because it is find,
just like we used here.
Right?
So, we're creating a find query,
and so, therefore, the find hook is then executed.
Then, in here, since it is query middleware,
the this keyword points to the query.
And so, to that query, we can then chain
yet another find method, right?
And in there, we then filter out the secretTour
using this filter object.
Okay?
So basically saying that we only want tours
where the secretTour is not equal to true.
And it would actually work the same
if we simply said here, false,
but I find it cleaner like this.
Okay, there is now just one thing that we need to fix,
because right now this middleware is running
for find, but not for find1.
Okay?
So let me show you the problem that we might get with that.
So, uh, let's take the ID of the Secret Tour.
So we have the Secret Tour here,
and that's its ID, and so if we get tour,
then that should give us the Super Secret Tour here.
Right?
And so you see, that that filter
that we just built in the middleware
is not working for this command.
Okay?
And that's because the handle function for this route,
as you might remember,
is using findByID, which, behind the scenes,
is find1, and so it's different from find.
All right?
So, we need to specify the same middleware also for find1.
Now, there are two ways of doing that,
and the first one would be simply to go ahead,
copy this code, and then put find1 here.
But that's not really good,
and so instead we're gonna use a regular expression.
And that is quite easy, and so ...
And let me actually just duplicate this here,
comment this piece out,
and then, remember, a regular expression
starts and ends with a slash.
And then in here, what I want to say,
is that this middleware should be executed
not only for find,
but for all the commands that start with the name find.
Okay?
So, find, and find1, and also find1 and delete,
find1 and update,
and so all of these will now actually trigger
this middleware function that we have here.
All right?
So, no need for that.
This regular expression here is way better.
So again, this here simply means
all the strings that start with find.
And actually, since we're here,
let me show you the Mongoose documentations again.
So, when we go here, to middleware in these guides,
you'll then have an overview of all the middleware.
For example, document middleware
is the one that we talked about before,
which runs, for example, on save.
But in fact, document middleware can also
run for stuff like remove.
All right?
Or for validate.
Okay, but I didn't talk about these because
usually the one that we always use is just for save.
Now, query middleware can run for
all of these query functions.
So find, as we just specified,
and now, with our regular expression,
it's also gonna run for find1,
findOneAndDelete, findOneAndRemove,
and findOneAndUpdate.
Anyway, moving back to Postman now,
this should now have fixed this situation here.
So when we now run this query,
it should have no results.
And indeed, it doesn't.
Because, remember, we were trying to get a secretTour.
So the tour with this ID here is secret,
but we never want the secretTours
to show up in any query.
And so that's why, right now, we get zero results.
Okay?
So that works now.
Don't save this,
and now, back to our code.
Let's now actually also specify
a post middleware for find.
So, tourSchema, and then, just like before, post,
and then let's use our regular expression here again
for anything starting with find.
And then just a regular function,
and then here, in the post-find middleware,
we actually get access to all the documents
that we returned from the query.
So let's call that Docs.
All right?
So, remember that this middleware is gonna run
after the query has already executed.
And so, therefore, it can have access
to the documents that were returned.
Again, because that query has
actually already finished at this point.
So let's log these documents to the console,
so docs,
but I also want to do something else here.
So basically, let's create, kind of,
a clock to measure how long it takes
to execute the current query.
All right?
So, how are we gonna do that?
Well, quite simple.
We can simply set a property onto the this object,
because this query object is really just a regular object.
Of course it has access to all these methods,
such as find,
but we can also use it
to set any property that we want on it.
So, again, just a regular object.
And so here we can say this.start
should be the current date.
Okay?
And so this will set it as the
current time in milliseconds, okay?
And so then, here, in the post middleware,
which is gonna run after the query has executed,
we can then subtract the current time
minus the start time.
So console.log,
query took,
and now the current date
minus this.start,
milliseconds.
Okay, let's give it a save,
and so let's now try to run a query and see our results.
Okay?
So, here we get all our tours,
here we have the console,
and let's actually clear that.
There's a lot of stuff in there.
Request again,
and so here are all the results.
So basically these are all the tours that were found,
and so that's the result of console.log of the docs.
So basically the documents that matched the query.
But I'm now interested in this first console.log,
and so, here we see "Query took 120 milliseconds,"
and so that was basically the time that passed
from the beginning, where we defined this,
to after the query has executed, at this point in time.
All right.
And that is all that I had to show you for now
about query middleware,
and just like before,
of course we're gonna use this a lot
throughout the course,
because, as I mentioned in the beginning,
middleware is really a fundamental concept
that's really important for a lot of stuff
that we need in our applications.
So we're almost done talking about middleware.
Next up is the last one,
which is aggregation middleware.