00:06
we're ready to actually search against it. So within our terminal here, let's create a controller so that we can do exactly that. We'll call this search and add an index
00:16
and an autocomplete method inside of that controller. The index is going to be in charge of actually rendering out the page that we'll use to submit a form to search with, and then the autocomplete is going to be
00:26
a fragment return that's actually going to perform a search and render out the listed search results right there on the server side. So we'll hit Enter to create those,
00:35
and then Node.Ace.MakeValidator, call that search as well, and then we'll go ahead and dig into our text editor, and let's start by jumping into our routes.
00:44
Then since I like all my routes to just be on one long line, I'm going to go ahead and add in real quick an eslint disable, just to disable all of that.
00:53
Then underneath our jumpstart route, we'll go ahead and add in a router.get for slash search that uses our search controller,
01:01
and that one will route to our index method as search index, and then we'll have another one for post to search,
01:09
and then we'll do autocomplete, use our search controller, and it's autocomplete method as search autocomplete.
01:17
Then within our resources, views, pages, let's right-click and make a new file. We'll put this inside of a search folder and call it index.edge.
01:26
Now jumpstart also included along with it some EdgeJS components like a layout component, some form utilities, a button, card, et cetera.
01:35
So we're going to go ahead and make use of those. If you want to see exactly what they're doing, feel free to dig into any of those component files to see what all they offer. So we'll go ahead and wrap this in a layout.
01:45
So we'll do layout there and end it, and then just so that our search results don't get too wide here, or our search bar doesn't get too wide, we'll do a max width, 5XL, MX auto,
01:55
and then inside of this div, we'll do a form that has a method of post and an action of form.post
02:04
that points to our search autocomplete. This form post is also a utility added by jumpstart. If we just take a look at form service, there's this edge form service,
02:14
and that is exactly what this form is. It just has different methods for post, put, patch, and delete that accept the exact same argument as our route helper would,
02:22
but it just includes some utilities to add additional HTTP method spoofing, which it has also enabled automatically for us. Okay, so we have our form set up there.
02:32
We need to also end that, and inside of it, we'll have a form group that has a name of term
02:40
and a class of bg-white, padding of three, make it rounded, margin top of six, and then we'll make it relative as well for our autocomplete dropdown,
02:50
and then we need to end that out, and we'll add in a form input inside of there that is of type search with a placeholder.
02:59
What are we searching for today? Okay, so the form group takes in the name. It will handle error displaying and whatnot,
03:07
and then it will pass that name down through to our input that will use it automatically, so that's why we don't need to also pass a name into the input here as the group will take care of that. Okay, more work to be done here,
03:17
but that's where we're gonna leave it for right now. Then let's also add a new view folder, so we'll right-click on Views, New File, and we'll add a folder called Fragments.
03:26
This is gonna house our server-rendered fragments, which for us here in the series is just gonna be our search-autocomplete.edge file,
03:34
and inside of this file, let's just start by taking a look at the raw data that we're gonna get back from Melee Search, so we'll inspect our books, and we'll give that a save.
03:43
All right, let's then move on to our search validator, and this is just gonna be a simple one, so we'll export const search validator
03:52
equals vine compile with a vine object and a single property called term that is a vine string, and it's also optional, and that's it for our validator.
04:02
We can then go ahead and move into our search controller here. Our index method is going to be super simple, just takes view in out of our HTTP context
04:12
and returns back view, render, pages, search, index, having a really hard time spelling search today, there we go, and then our autocomplete method
04:21
will take in the request as well as view since it will server-render our results, so this is going to be const term
04:29
equals await request validate using our search validator. The first thing that we're gonna wanna do
04:36
is use this term to search our book index with that term, so we'll do const books equals awaits,
04:44
import our search service, and then use our search privileged instance, not our admin instance as we just wanna search this index.
04:54
So we'll specify the index that we wanna search here as books, and then we'll perform a search with our term just like so. Then using what we get back as our books,
05:04
we'll go ahead and return view render, reach through to our fragments, search autocomplete file, and pass the books into it.
05:13
So if we real quick just take a look at the type for our books, you'll see that it's a search response record any search params. Let's see what that looks like in practice. So we'll give this file a save,
05:23
and let's jump back into our terminal, npm run dev to build our server up, let's wait and make sure that goes okay, sure does, great. So we can jump back into our browser then,
05:31
and let's open back up our project and switch over to our new search page. So within our search bar here, we can search for, let's see, we saw that we got back results for Martha
05:41
within our Melee search dashboard. So we'll search for Martha here. And since this is currently just a simple post, we're now at our search autocomplete path,
05:50
taking a look at just the rendered results here from our inspect. And we can see that the search results that we get back have hits, which is an array of the documents
05:59
that actually match our search results. And then if we continue to scroll down, scroll down further, let's just go down to the bottom here. We'll see that in addition to hits,
06:07
we also have the query that was searched with, how long it took to process, the limit that it will query per page, which it defaults to 20, the offset for like pagination.
06:17
So if you wanted to go to the next 20, that would be the offset there. And then the estimated total number of hits. So that's an estimated total there. So since we got back 21, if we were to offset 20,
06:27
we would then just get a result of one as that second paginated data list, cool. So what we can do is utilize this information
06:35
that we now have to build out our autocomplete list. And here we'll just start by focusing on our books. So if we jump back into our text editor, what we wanna do is go back down
06:45
to our autocomplete fragment and replace this inspect with some actual usable markup. So we'll wrap this in a div. We'll expand on this further whenever we add on poly in.
06:55
And inside of the div, we will do an at if, and we pass books down as a variable. So we'll do books. We saw that we got back an array of hits. So we do hits length to check and see
07:05
whether or not we got any results, and then end our if. Inside of here, we'll do a ul with a class. I'm gonna give this a max height of 300 pixels
07:14
over flow y auto and end that out there. Then we'll do an li with a class padding of four,
07:22
padding bottom of two, text extra small, opacity 60, tracking wide, flex item center, gap two, end our li. And this is just gonna be like a title
07:32
saying that this is the book's results. And then we will go ahead and add in a span that will display the number of books that we got back.
07:40
So that's gonna be our books.estimatedTotalHits. And then underneath this li, we want to loop over each book in our books.hits.
07:50
So end our each there, and then we'll do another li with a class, p4, not last, child, border, bottom,
07:58
border, slate, 300, hover, bg, slate, 50. And then we'll end that li, div, class,
08:06
text, extra small, uppercase, opacity 60. And this, we will go ahead and list out the book's genres.
08:13
So we do bookGenres.map, map over each genre. And we've indexed both the genres ID and name. So we just want to display back the genre's name to the user there.
08:23
For that, underneath the genre, we will then add in the actual book's name. We'll also do font, semi-bold there, and add in the book.title.
08:32
And then last, we'll put a paragraph there with the blurb. So we'll just do text extra small on that and add the book.blurb there.
08:41
All right, that should be it for our book markup. Let's give that a save, jump back into our browser. And since this was a post, we're gonna need to go back and then we can hit enter again
08:51
to submit a fresh results set. So we can now see that we're getting back some formatted data here. It just looks like raw HTML because the fragment that we're rendering
09:00
doesn't have tail and CSS included with it. That will come after we integrate unpoly to make this a little bit more functional, which is what we're gonna do in the next lesson.