So Today I have added a new feature to the Blog!!!
I added a search engine to the blog so it is easier to navigate and also find specific things that I have previously talked about.
It works by searching through a JSON Object provided by Hugo which contains all the posts contents.
We do that by using the following lines to create the JSON Object:
{{- $result := slice -}}
{{- range (where .Site.RegularPages "Section" "!=" "") -}}
{{- $data := dict "title" .Title "content" .Plain "id" .File.UniqueID "url" .Permalink "tags" .Params.tags -}}
{{- $result = $result | append $data -}}
{{- end -}}
Let’s go through the Hugo code:
{{- $result := slice -}}
This line creates a slice which is an array in Go.
{{- range (where .Site.RegularPages "Section" "!=" "") -}}
This line loops through all the pages on the site and checks if the page is a regular page and not a section.
(e.g. /post/quote1.md is a regular page and /post/ is a section)
{{- $data := dict "title" .Title "content" .Plain "id" .File.UniqueID "url" .Permalink "tags" .Params.tags -}}
This line creates a dictionary which is a map in Go and it stores the title, content, ID, URL, and tags of the page.
{{- $result = $result | append $data -}}
This line appends the dictionary to the slice.
{{- end -}}
And then for us to get the JSON we use the following line:
{{- $result | jsonify -}}
But the good thing with using Hugo is that we can put the JSON in a variable and then we can use it in our JavaScript code.
const searchData = JSON.parse("{{ $result | jsonify }}")
Then it’s just finding the word or phrase given in the objects’s content and then displaying the results.
But we also need a way to get and display the results.
For that, we will use a form
element.
<form action="/search" method="GET">
<input type="text" id="search" placeholder="Search...">
<input type="submit" value="Search">
</form>
What is a form element in HTML?
A form element is an element that is used to get user input. It can be used to get text, numbers, dates, etc.
It also can redirect the user and give the input as a query string.
You may have noticed that we are using the argument action="/search"
in the form element.
That is because this specifies the page that the form will redirect the user to.
So we can create a new page ("/search" in this example) and then we can use the query string to get the input.
What is a query string?
A query string is a part of a URL that is used to pass data to the server. e.g. ‘https://blog.arisamiga.rocks/search?v=potato'
In this example the query string is ?v=potato
and the variable is v
and the value of the variable is potato
.
So we can use the query string to get the input from the user.
const urlParams = new URLSearchParams(window.location.search);
const search = urlParams.get('v');
Then we can make a search
variable to go through the JSON and find the results.
// Search function
const search = (value) => {
const results = searchData.filter((data) => {
const regex = new RegExp(value, "gi")
return data.title.match(regex) || data.content.match(regex)
})
return results
}
search(search)
The code above uses Regex to find the word or phrase in the content of the object.
What is Regex?
Regex or Regular expression is a sequence of characters that specifies a match pattern in text. It is used in many programming languages and it is very useful.
Let’s go through the code!
const results = searchData.filter((data) => {
})
This line filters the JSON and returns the objects that match the condition.
const regex = new RegExp(value, "gi")
This line creates the condition we want and it is used to find the word or phrase in the content of the object.
“gi” means that it will be case insensitive (So it will match “Potato” and “potato”) and it will be global (So it will match all the words or phrases).
return data.title.match(regex) || data.content.match(regex)
This line returns the objects that match the condition.
You can also use different patterns as values. I recommend checking out this website to learn more about Regex: https://regexr.com/
Then all we have to do is get the index of the object and display the rest of the information like the title, content, and url.
const results = search(search)
const resultsContainer = document.getElementById("results")
results.forEach((result) => {
const title = result.title
const content = result.content
const url = result.url
resultsContainer.innerHTML += `
<div class="result">
<h3>${title}</h3>
<p>${content}</p>
<a href="${url}">Read More</a>
</div>
`
})
Now a Thing that I want to point out here is that if a user gives input that is html code it will be displayed as html code not as text which is not good.
To prevent that we can use the following function to escape the html code:
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
What this does is that it replaces the html code with the html entities. If you want more information about html entities you can check out one of my previous posts Its Summer Time! ☀️.
So we can use this function to escape the html code and then we can display it as text.
const results = search(search)
const resultsContainer = document.getElementById("results")
results.forEach((result) => {
const title = escapeHtml(result.title)
const content = escapeHtml(result.content)
const url = result.url
resultsContainer.innerHTML += `
<div class="result">
<h3>${title}</h3>
<p>${content}</p>
<a href="${url}">Read More</a>
</div>
`
})
If you also want to mark so you can see the word or phrase that you searched for I have made something similar here Updates to the NDK Search Engine!
Also If you want to show the content from the point where you first find a word you would use something like this before actually displaying the content:
results.forEach((result) => {
if (result.content.length > 150) {
const regex = new RegExp(value, "gi")
result.content = result.content.replace(regex, (match) => `<mark>${match}</mark>`)
const firstMark = result.content.indexOf('<mark>')
result.content = result.content.substring(firstMark, firstMark + 250) + "..."
}
})
This will mark the word or phrase that you searched for and then it will display the content from the point where you first find the word or phrase.
It will also display only 250 characters from the point where you first find the word or phrase.
It uses the substring
function to get the content from the point where you first find the word or phrase and then it adds the rest 250 characters.
result.content = result.content.substring(firstMark, firstMark + 250) + "..."
And that is basically how the search engine works!
If you want to make your own Search engine for your Hugo website I recommend checking out these posts:
https://makewithhugo.com/add-search-to-a-hugo-site/
https://theorangeone.net/posts/hugo-website-search/
https://ruddra.com/add-search-functionality-hugo/
I hope you enjoyed this post and like the new addition to the blog! (I know it will help me a lot) Have a nice day! And thanks so much for reading :D