Introduction
Hugo
is a static website constructor, based on GO
. In this article we will realize a search tools for the posts in your blog. It is well introduced in the official doc: [hugo doc]. Here we use the commercial search service: Algolia
[Algolia site] .
Algolia has a free plan for personal use, which provides 10,000 records and 100,000 indexing operations per month. But it is enough for those personal website to use.
This article follows the post of Yangming’s Blog [Hugo&Algolia] .
Sign up to Algolia
It is allowed to sign up to Algolia with the authorization of Github or Google accounts. And by the default, Algolia will construct an app to us. For new user there is a 14-day-free-trial with much more records and permitted operations. One can get to learned during these two weeks.
The direct interface for the user of Algolia is its Dashboard
[Dashboard]. And in the indices page we can find how many indices have been used.
Algolia provides three ways to add records: manual, upload JSON
file, and API. Here we will use the third way: API
to add our records.
Tool: hugo-algolia
To upload the records of the posts, we need to construct the JSON
file to describe the content data. There is a open-source tool hugo-algolia
to handle this easily. The github-site is : [Github site].
This tool is used to generate and send indices from Hugo static sites for use with Algolia
And it supports YAML, JSON, and TOML front matter.
The installation is easy to do with npm
:
npm install hugo-algolia
remember to let it executable with chmod +x
And one can simply use it with TOML front file to generate the JSON file:
hugo-algolia --config ./config.toml
It will generate a JSON file in the $hugoroot/public` folder, in which all posts in $ hhugoroot/content will be listed.
One can also use the -s
flag to send them to Algolia. However this tool can not read the appID
and key
in TOML
file, we can make some hack on it. A temporal adjustment is edit the file $node_modules_prefix/lib/node_modules/hugo-algolia/lib/index.js, rewrite the function
HugoAlgolia.prototype.setCredentials` :
//$node_modules_prefix/lib/node_modules/hugo-algolia/lib/index.js
HugoAlgolia.prototype.setCredentials = function() {
/*
Original code, one can find that it called the module "matter" to handle the
YAML file. Now YAML can handle the TOML too with the same usage, but the
author of hugo-algolia has not correct it yet.
const configMeta = matter.read(self.pathToCredentials);
const creds = configMeta.data.algolia;
if (creds) {
self.algoliaCredentials = {
indexName: creds.index,
key: write_key && write_key !== undefined ? write_key : creds.key,
appID: creds.appID
};
self.localIndexName = creds.index;
}
*/
/*
Here we directly write the "index", "key", and "appID" here so that them can
be send to Algolia correctly.
*/
self.algoliaCredentials = {
indexName: "Your Index Name",
key: "Administrator's Key",
appID: "The Application ID"
};
self.localIndexName = "Your Index Name";
};
With the adjustment, we can use the command:
hugo-algolia -s --config ./config.toml
at hugo root folder to generate the JSON indices file and send them to Algolia.
After this process we can find them under the indices
page in the dashboard.
Front-end Setup
Now we have submitted posts we want to Algolia. Then we need to make a front-end UI to use it. It is different for different hugo themes. Here we use a simple one:
we will add a little icon which is a <a>
element actually, and when one click on it, there will be a modal
subpage by the bootstrap 4
library. And there will be a input dialog in it. Then we can use it to search the posts.
We need to add the bootstrap 4
in the site, and because of the dependence, we need also add the jquery
lib. Edit the $themepath/layouts/partials/head.html` file:
<!--$themepath/layouts/partials/head.html-->
<!--Bootstrap-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
One need to remove the origin bootstrap css
importation if exists.
Then we can make the front-end ui:
First we need to add a file like $themepath/layouts/partials/search.html`:
<!--$themepath/layouts/partials/search.html-->
<div class="aa-input-container" id="aa-input-container">
<input type="search" id="aa-search-input" class="aa-input-search" placeholder="Search for titles or URIs..." name="search" autocomplete="off" />
<svg class="aa-input-icon" viewBox="654 -372 1664 1664">
<path d="M1806,332c0-123.3-43.8-228.8-131.5-316.5C1586.8-72.2,1481.3-116,1358-116s-228.8,43.8-316.5,131.5 C953.8,103.2,910,208.7,910,332s43.8,228.8,131.5,316.5C1129.2,736.2,1234.7,780,1358,780s228.8-43.8,316.5-131.5 C1762.2,560.8,1806,455.3,1806,332z M2318,1164c0,34.7-12.7,64.7-38,90s-55.3,38-90,38c-36,0-66-12.7-90-38l-343-342 c-119.3,82.7-252.3,124-399,124c-95.3,0-186.5-18.5-273.5-55.5s-162-87-225-150s-113-138-150-225S654,427.3,654,332 s18.5-186.5,55.5-273.5s87-162,150-225s138-113,225-150S1262.7-372,1358-372s186.5,18.5,273.5,55.5s162,87,225,150s113,138,150,225 S2062,236.7,2062,332c0,146.7-41.3,279.7-124,399l343,343C2305.7,1098.7,2318,1128.7,2318,1164z" />
</svg>
</div>
<script src="{{ "https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/algoliasearch.min.js" | absURL }}"></script>
<script src="{{ "https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/autocomplete.min.js" | absURL }}"></script>
<script>
/*here you need to write your appID and apiKey(not administrator key!)*/
var client = algoliasearch("Application ID", "API Key");
var index = client.initIndex('Your Index Name');
autocomplete('#aa-search-input',
{ hint: false}, {
source: autocomplete.sources.hits(index, {hitsPerPage: 8}),
displayKey: 'name',
templates: {
suggestion: function(suggestion) {
console.log(suggestion);
/*here the link format might be need to correct according to your site */
return '<span>' + '<a href="/' + suggestion.uri.toLowerCase() + '/">' +
suggestion._highlightResult.title.value + '</a></span>';
}
}
});
</script>
Where the svg
element is a little magnifier icon.
Then we need to make a entry. For AllinOne
theme: [AllinOne homepage] , we will set it on the left of navbar
. Edit the file $themepath/layouts/partials/site-navbar.html, add this
element at the end of the
in the label
<!--$themepath/layouts/partials/site-navbar.html-->
<a data-toggle="modal" data-target="#modalSearch" style="outline: none;">
<svg class="aa-input-butn" viewBox="654 -372 1664 1664">
<path d="M1806,332c0-123.3-43.8-228.8-131.5-316.5C1586.8-72.2,1481.3-116,1358-116s-228.8,43.8-316.5,131.5 C953.8,103.2,910,208.7,910,332s43.8,228.8,131.5,316.5C1129.2,736.2,1234.7,780,1358,780s228.8-43.8,316.5-131.5 C1762.2,560.8,1806,455.3,1806,332z M2318,1164c0,34.7-12.7,64.7-38,90s-55.3,38-90,38c-36,0-66-12.7-90-38l-343-342 c-119.3,82.7-252.3,124-399,124c-95.3,0-186.5-18.5-273.5-55.5s-162-87-225-150s-113-138-150-225S654,427.3,654,332 s18.5-186.5,55.5-273.5s87-162,150-225s138-113,225-150S1262.7-372,1358-372s186.5,18.5,273.5,55.5s162,87,225,150s113,138,150,225 S2062,236.7,2062,332c0,146.7-41.3,279.7-124,399l343,343C2305.7,1098.7,2318,1128.7,2318,1164z" />
</svg>
</a>
And add the following <div>
after the <nav>
:
<!--$themepath/layouts/partials/site-navbar.html-->
<!-- Modal -->
<div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Search</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{{ partial "search.html" . }}
</div>
</div>
</div>
</div>
You need also add a CSS
file in which to assign the style of these elements:
CSS file
/*#themepath/static/css/search.css*/
@import 'https://fonts.googleapis.com/css?family=Montserrat:400,700';
.aa-input-container {
display: inline-block;
position: relative;
width: 100%;
}
.aa-input-container span,.aa-input-container input {
width: inherit;
}
.aa-input-search {
width: 300px;
padding: 12px 28px 12px 12px;
border: 2px solid #e4e4e4;
border-radius: 4px;
-webkit-transition: .2s;
transition: .2s;
font-family: "Montserrat", sans-serif;
box-shadow: 4px 4px 0 rgba(241, 241, 241, 0.35);
font-size: 11px;
box-sizing: border-box;
color: #333;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.aa-input-search::-webkit-search-decoration, .aa-input-search::-webkit-search-cancel-button, .aa-input-search::-webkit-search-results-button, .aa-input-search::-webkit-search-results-decoration {
display: none;
}
.aa-input-search:focus {
outline: 0;
border-color: #3a96cf;
box-shadow: 4px 4px 0 rgba(58, 150, 207, 0.1);
}
.aa-input-icon {
height: 16px;
width: 16px;
position: absolute;
top: 50%;
right: 16px;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
fill: #e4e4e4;
}
.aa-input-butn {
height: 16px;
width: 16px;
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
fill: #e4e4e4;
}
.aa-hint {
color: #e4e4e4;
}
.aa-dropdown-menu {
background-color: #fff;
border: 2px solid rgba(228, 228, 228, 0.6);
border-top-width: 1px;
font-family: "Montserrat", sans-serif;
width: 300px;
margin-top: 10px;
box-shadow: 4px 4px 0 rgba(241, 241, 241, 0.35);
font-size: 11px;
border-radius: 4px;
box-sizing: border-box;
}
.aa-suggestion {
padding: 12px;
border-top: 1px solid rgba(228, 228, 228, 0.6);
cursor: pointer;
-webkit-transition: .2s;
transition: .2s;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.aa-suggestion:hover, .aa-suggestion.aa-cursor {
background-color: rgba(241, 241, 241, 0.35);
}
.aa-suggestion > span:first-child {
color: #333;
}
.aa-suggestion > span:last-child {
text-transform: uppercase;
color: #a9a9a9;
}
.aa-suggestion > span:first-child em, .aa-suggestion > span:last-child em {
font-weight: 700;
font-style: normal;
background-color: rgba(58, 150, 207, 0.1);
padding: 2px 0 2px 2px;
}
And add it to the $themepath/layouts/partials/head.html` to let it contain the css file:
<!--$themepath/layouts/partials/head.html-->
<!--Search-->
<link rel="stylesheet" href="{{ "css/search.css" | absURL }}" />
There might be a little bug of the bootstrap modal, which let the padding-right
of body
increase at each time to use the modal. To correct this, we can add a line to the $themepath/static/css/main.css` :
body {
padding-right: 0px !important;
}
Result
After all, we will have a search icon in the left of the navigation bar. It will be looked like: