[MUSIC]
>> I'm really ready to do this
'cause I've been working on my website
in Nuxt.js and I've got it to the point
where I feel like I could launch it,
but I think it would be cool to have the ability
to search the site, like old content.
There's not a whole lot of posts on there right now,
but eventually I'm gonna be adding YouTube videos to it.
I'm gonna be adding the Twitch streams to it,
more blog posts, of course.
So I wanna be able to search that content over time,
but here's the project I want us to tackle today.
Using Azure Cognitive Search, anytime we deploy our website,
we'll trigger over to Cognitive Services that says,
"Hey, you need to rebuild your indexes of content
on the site."
Let it rebuild it, and then we'll build
some kind of a front-end search
on the site that actually communicates back with Azure
to load that stuff in.
Okay, so this is my new website.
This is not, this is actually VS Code,
but I'll show it to you.
Let's run it, npm run dev.
Let's see, while that's loading,
let's bring it up over here.
Localhost 3000.
Here it is.
The mast only shows four tablets and higher.
So like if you're on a mobile size, it goes away.
And this jazz is all responsive.
It's showing the latest two blog posts
and then I added these Let's Get Social buttons.
As I add these blog posts,
one thing that's kinda cool, is it loads 12 by default.
But then as I scroll down, it'll load in the next 12
and the next 12 and the next 12 on the fly.
So it's got infinite scrolling going down the page.
On the homepage, it's got the latest two posts
but what I'm gonna do is I've got this bar here
for Let's Get Social,
and that actually is responsive, too, I think.
If you go smaller, it starts stacking 'em.
But I wanna add in like another section
between that and the footer, and I may even put it on top.
I don't know.
But like whenever I post a new video to YouTube,
I wanna be able to have like several panels
across of the last two or three videos
and just have it constantly update,
so that's to come.
But all of this content is a lot of stuff, right?
So if you wanted to be able to like find something
on the site, you either need to know
right where you're going or you need to know
right where you're going.
So I would like the ability to add search somewhere.
I don't know where I wanna put it
but I know I wanna try and use
Azure Cognitive Services to do it.
So let's see if we can do that today.
Spoiler alert, I'm pretty sure we can
because we're good enough, we're smart enough,
and doggone it, people like us.
Anyway, let's log into Azure.
Be sure to hit exclamation Azure in chat
and go visit that link.
It's just a link to a YouTube playlist,
but it's got all of their AI stuff kind of overviewed
and shows you how to do it.
It's really sick.
Integrate search, that's exactly what we wanna do.
This is how-to guides.
Can you show me something like, ooh, a sample.
We're using JavaScript in this case, cognitive, cog,
Cognitive Services,
create.
The name, let's call it B-B-B-C search,
or cog, cognitive services.
I guess, I don't know.
Pricing tier, I would prefer free.
Can I do free?
Review and create, okay.
What did it tell us to do next?
Copy a key and endpoint.
Get the URL, okay.
Let me get the greatest code editor known to man, Notepad.
Next in settings, keys, get the admin key
for full rights on this service.
It will hide those by default.
It will, so I'm gonna copy that,
but I'm gonna paste it where you can't see it.
Ooh!
Okay, npm, I, that, what I'm thinking is,
like they're running this and loading these documents,
but because we're using Nuxt,
we're loading in all of our content when it gets started.
So we could do all this then.
Let's do that!
Let's create like a module
and we'll call it azureSearch.js.
Yeah, I'm gonna go ahead
and add it into this barrel roll here.
Import azureSearch from, believe it or not,
azureSearch and export it.
AzureSearch, perfect.
Let's bring that in and then,
yeah, I guess it's okay.
Next it's doing this rigamarope.
Let's, I don't know what that is,
but I'm gonna copy it in just for now.
IndexDefinition, IndexClient,
I'm just gonna copy all this code in,
then we'll go figure it out.
We copy and paste and then we let God sort 'em out.
Copy that.
Doo doo doo doo, yeah, okay, perfect.
Perfect, doo doo doo doo.
I don't think I need that at all.
We're gonna load in a file, create an index
based on that key, get rid of this console log stuff.
We will have to fill in this rigamarole here, right?
Todo, with the actual index.
I think I wanna do like constant,
delete index if it exists equals async,
that, fat arrow, that mug,
and then down here at the bottom, I'm gonna say something
like export,
buildSearchIndex,
buildSearchIndex equals async something-or-other.
What the heck was that?
Export default that.
Ah yeah, we'll put the consoles in there.
Console log deleted,
index.
So create a resource, search service, Cognitive Search.
Oh, wait, wait.
So I'm gonna have to do these.
If I delete the index first,
I'm gonna have to upload that file.
So we gotta come back and fix that.
We're gonna delete the index.
So the first thing we're gonna do is when we build it,
what I'm thinking is when we're building this Nuxt site,
it's static, right?
There's a point where we know every route
and all the content on all those pages.
So what we'll do is, during that build process,
we'll call over to Azure and say,
"Hey, delete the index for the site,
"and here's all the data for the new index."
And then once it's all there,
it's in sync with whatever site is deployed.
So as part of that deployment process,
we rebuild that index in Azure Cognitive Search,
and then we can just cull it from that site.
Index client.
So they had this let index equals create index, okay?
Okay.
So we'll do that
and now it's a matter of loading the documents, okay?
So we do need them,
and we probably wanna, I bet we wanna add that
after the fact here, like after we create that new one.
Getsearch, oh, perfect!
That's even better 'cause I've got my index.
Perfect, so we're creating an index client
and this is the method we'll actually call
when we build the site.
And the first thing it's gonna do is delete that index
if it exists, then it's gonna go create the new index
and give us a search client based on that.
Okay, copy this, paste it in.
Doo doo doo doo.
So in that case, I'm gonna need
that original schema thing up here.
Copy that, new file.
Let's call it azureindex.json.
I'm gonna paste that in.
Let me talk about how this site gets built, okay?
When we build a site, it goes through and reads
this whole site.
It's going through all that markdown in YAML
within that content folder and building slugs
and paths, and is building like a little,
almost like a little document database.
It really makes a RESTful API for you
to access this stuff.
Before it inserts that file,
before it inserts into its database of content,
I'm doing some stuff, like I'm cleaning up
what the slugs are and the paths are
to make 'em nicer in the URL.
One of the things I go through and do, too,
is I call over to Cloudinary and I call
this copy assets method that I wrote.
This copy assets goes through the document
and finds any place where there's an image,
and it's finding that image path and says,
okay, I'm gonna go see if that image exists.
Oh, well heck, there it is right there.
It sends that image up to Cloudinary and then it checks
and looks at the document and calculates,
how long would it take to read this blog post?
And then it loads it into the database.
What I'm thinking is at this point,
we may could go ahead and send this into,
if not on this hook, on one of these build hooks.
We'll know all of these routes and we actually
have the body of the document here,
so we could send it all into that deal.
So I think we can upload one document at a time.
That would be okay.
So then in here we could just say,
hey, for each one of these documents,
load 'em into the search.
Let's call this, I don't know, bbb-content,
I have no idea.
Slug, it will be the key.
Let's call it title.
Searchable? Yup.
So normally I build this to be static
but right now it's not
so we can actually see the data.
So let's look at like blog and see what the data is.
Okay, so like tags we'll use,
slug is gonna be important.
Title we'll have.
Slug description, we won't have that.
Category, mm, I don't have categories right now.
Tags is a collection of string.
That's fantastic.
Yeah, I've got tags and it is in fact
a collection of strings.
Parking, this is hotel stuff.
Get rid of this stuff.
Ooh, let's do last updated
or should we do date created maybe?
Created, rating,
oh, they're all bad.
I think date would be better rather than created at.
Let's just say date.
Let's just call it date.
We probably need a type, don't we?
Filterable, sortable, create the index.
We'll delete it if it exists.
We'll create the index, create a search client,
and then we're gonna be loading in documents.
But here what I'm thinking is,
because I'm already generating,
I know all these routes already.
That will actually give me all the data
about those objects as well.
So I could build that index right here.
Constant or search index documents, let's call it.
So I'm gonna create the documents in here,
posts.map(post) and I'll do the same
for talks and I'll do the same
once we add Twitch streams to it
and once we add YouTube videos to it.
I'll just come in here and add that
to all get indexed in the same thing.
And then I'll say, bring in azureSearch up here.
And then as part of the build process,
say azureSearch.buildSearchIndex
with the search index documents that I just built.
That thing can say delete to index
if it exists, create a new index,
upload those documents which are searchIndexDocuments,
and that'll log out how many it thinks it added.
Ooh, this gonna get fancy.
If body.children like bodychildren.foreach(tag)
or child, let's say.
If body.type equals text,
return, body.value.
Easy enough.
Return results equal an empty array,
results.results.push.
So right now I'm feeding it the info from my blog.
So these are all the posts.
It's all this data.
So we'll see if that 19 documents is what we think it is.
Oh, there they are, 19.
Holy crap!
Okay, so then one of the things we're talking about doing
is as we build this site,
where we're gonna have like two or three
of the latest YouTube videos on there,
maybe the last two or three of our Twitch streams on there,
we're gonna load 'em in as YAML
for each stream and each video,
so we'll just come in here then when we add it
and say, okay, and also, you know,
streams.map and that stuff, and you know
videos.map and that stuff.
Let's say search.vue.
We'll create a component that has this built into it
and then we can just plug this search component
wherever we want it to do.
Data, return, searchfor is null.
Just do like an input, type is text, searchfor,
let's also do like searchoptions
and we'll just do something like this.
We definitely wanna get back to route, right?
So we wanna get route more than, that's really important.
I'll probably title.
Let's see, we brought in OData up here.
We need to actually bring that in here
so they won't need to do in search.
We'll do something like that.
Let's do that, let's copy there.
Copy their thing again.
This would be this.searchFor.
SearchClient equals something like that.
We're gonna need a function running in our site
to answer this.
Luckily I know how to write the functions.
I'm gonna copy and paste that one and call it search.
In that case, let's move this out of there
and put it up here.
Constant Azure Cognitive Search endpoint
equals process.env.something.
I don't know what it is.
We'll figure it out.
Constant ACS process.env.something else.
Get rid of all this.
Good day to you, Sir.
Let's go back to where their code
and see where they said how to do it.
Search client equals end point.
Okay, copy that.
Paste it like a boss.
And we'll use ACS endpoint and API key name,
index name, I think we'll just call it bbb-content.
Let's go over to our ENV sample.
It was called search API key.
There, and endpoint.
Endpoint, perfect.
So there's our search options.
Searchfor, we're gonna have to define.
I don't think I want that in the query string.
I guess it doesn't matter.
Query string, queryStringParameters.search,
searchfor, endpoint, so we're gonna create the client here.
There's some just default options we're gonna use.
It's gonna look for somebody coming in with a searchfor.
We're gonna call search.
We're gonna write to the console the values, for now.
We'll delete that out in a minute
and then we'll send back the results.
I'm gonna copy this and I'm just gonna paste it
down here in the bottom so I can see it.
Let's do a fetch on and that's the route.
It's not gonna be comments.
It's gonna be search, is what we called it.
Searchfor equals this.searchfor.
We'll call it constant response equals await that.
Let's just console dirp response for now.
I think we're done with that.
Holy frijole!
The first command would be nuxt with no arguments.
So that command is Netlify-lambda
serve functions are the arguments,
npm run dev.
Oh, there we go!
What the heck?
What the he...
What the heck?
Okay, so there, it's now running
and it should have a search box.
There it is.
Listen, it's always one of the best parts
of my week when I get to hang out
with all of you and learn things and build things together
and hang out and just have a good time.
Shout-out to Azure for sponsoring today's stream.
Have a good one.
Take care of yourselves.