This module serve the two following purposes:
- Phase 1: Produce an index endpoint on your site
- Phase 2: Produce the JS front end search with Algolia/InstantSearch.js
- Phase 3: Cater for MeiliSearch with InstantSearch.js
TND Search adds tooling for site search. It takes care of
- Building an index to feed to your search service
- Building the front of your search with InstantSearch.js for either Algolia or MeiliSearch
Requirements:
- Go 1.14
- Hugo 0.82.0
Make sure your project has been init
as a module.
# config.yaml
module:
imports:
- path: github.com/theNewDynamic/hugo-module-tnd-search
Through configuration.
# config.yaml
outputs:
homepage:
- HTML
- tnd_search
# + any other outputs needed on the homepage.
OR through homepage's Front Matter
# content/_index.md
title: Homepage
outputs:
- HTML
- tnd_search
# + any other outputs needed on the homepage.
User can control which search entries go the index, and how their data is structured.
By default, the Module include in the index every regular pages from the site using site.RegularPages
. In order to limit or extend this returned collection, one should create a returning partial on the project at layouts/partials/tnd-search/data/entries
.
{{/* layouts/partials/tnd-search/data/entries.html */}}
{{ return where site.RegularPages "Type" "posts" }}
{{/* layouts/partials/tnd-search/data/entries.html */}}
{{ $entries := where site.RegularPages }}
{{ $entries = where $entries ".Params.private" "!=" true }}
{{ return $entries }}
User can control how each entry data is structured before being published in the index. There is two approaches to this. The params
settings described below or by adding some partials to your projects.
By default, the data will be:
{
"created": "2019-10-10T08:37:57Z",
"draft": false,
"objectID": "ce3d65cdd6e3ced2e7e012d452ba289e",
"permalink": "http://mywebsite.com/products/gembucket-workshop/",
"relpermalink": "/products/gembucket-workshop/",
"title": "Gembucket workshop",
"type": "products",
"updated": "2019-10-10T08:37:57Z"
}
User can add entries or overwrite the above object's entries, but not delete keys from the default set above.
One can add to the default entry structure using the params
module settings.
To Add data to the aforementioned default entry data structure, one should create a returning partial on the project at layouts/partials/tnd-search/data/entry
.
{{/* layouts/partials/tnd-search/data/entry.html */}}
{{ $s := newScratch }}
{{ $s.Set "item" dict }}
{{ with .Params.search_summary }}
{{ $s.SetInMap "item" "search_summary" . }}
{{ else }}
{{ $s.SetInMap "item" "search_summary" .Summary }}
{{ end }}
{{ return $s.Get "item" }}
Settings are added to the project's parameter under the tnd_search
map as shown below.
# config.yaml
params:
tnd_search:
[...]
The params array lists which page's parameter should be added to the entry in the index. This allow user to add some custom parameters without the need of a returning partial.
tnd_search:
params:
- custom_summary
- city
- topics
Only algolia
and meili
are supported.
When building your index, the module will store a unique identifier under this reserved key.
The Module uses InstantSearch.js to bandle the frontend of the search.
This partial is to be loaded in your template to invoke the script(s). It takes a slice as parameter listing the scripts by their names. Anything but a slice as parameter will load all the scripts.
{{ partial "tnd-search/instantsearch/tags" (slice "default" "videos") }}
As the module supports multiple search integration (one for your video gallery, one for your main site search etc...). The instansearch
key of the module settings takes a list of scripts with their own settings.
The name
key defines the value by which they will be identified when using the tnd-search/instantsearch/tags
partial.
Four parameters are required, name
, indexName
, appId
, and apiKey
.
tnd_search:
instantsearch:
- name: main
indexName: default
appId: GMXXXXXXXQW
apiKey: 027xxxxxxxxxxxxxxxxxxxxxxxxx53e
tnd_search:
instantsearch:
- name: main
indexName: default
appId: https://test.search.com
apiKey: 027xxxxxxxxxxxxxxxxxxxxxxxxx53e
hitsPerPage
distinct
clickAnalytics
enablePersonalization
attributesToSnippet
snippetEllipsisText
Each widget must be
- registered in the module using the methods described below.
- Added to your template with an id matching its lowercase
name
parameter or optionalcontainer
parameter
Widget's name
key should match the name of the InstantSearch.js widget.
Widget option keys are matching InstantSearch.js's own except for cssClasses
which is shorttened to classes
.
For example, in order to add a simple Search Box and Hits:
tnd_search:
instantsearch:
- name: default
# [indexName, appId etc...]
widgets:
- name: searchBox
placeholder: 'Search now!'
classes:
root: 'search__box mb-8'
input: 'bg-transparent border-none w-full text-3xl'
- name: hits
container: '#search-hits'
classes:
root: 'bg-white'
list: 'list-reset'
The Module will then mount the searchBox
widget on your template's <div id="searchbox"></div>
element and the hits
widget on your template's <div id="search-hits"></div>
element with provided settings.
Even widget templates can be customized through yaml
using the widget available template keys and Hogan.js syntax:
- name: hits
classes:
root: 'bg-white'
list: 'list-reset'
templates:
item: |
<a class="flex justify-between border border-red-500 mb-4 px-8 py-2" href="{{ relpermalink }}">
<div>{{#helpers.highlight}}{ "attribute": "title" }{{/helpers.highlight}}</div>
<div>{{ type }}</div>
</a>
empty: |
<span class="block border-b border-black px-8 py-2">No results</span>
Many options are better handled through Javascript. In order to use javascript language to complement your widget yaml
options you need to:
- Create a javascript file at
/assets/tnd-search/instantsearch/widgets.js
which exports atndWidgets
object as such:
export let tndWidgets = {
hitsCustom: {
transformItems(items) {
return items.map(item => ({
...item,
type: item.type.toUpperCase(),
}));
},
}
}
2 add a js
key to your widget yaml regisrering with a value matching a tndWidgets
object's key.
- name: hits
# [...]
js: hitsCustom
These are experimental features whose current UX is likely to shift in the future. Use with caution.
requires Hugo 0.92.2
- You'll need the
MEILI_HOST
environment variable pointing to your Meili server - You'll need the
MEILI_PRIVATE_KEY
environment variable set with your Meili private key - You'll need to add the following partial somewhere in a template file: (preferably limited to one execution per build, ex:
.IsHome
test for monolingual setups)
{{ if .IsHome | and hugo.IsProduction | and (not site.IsServer) }}
{{ partialCached "tnd-search/update-index" "index-name" "index-name" }}
{{ end }}
- 🎉
WARNING: This does not delete documents from the index, only adds new ones and/or updates existing ones.
For local development, you can drop a git ignored data/env.yaml
file with the aforementioned keys and their value.
This project is maintained and loved by thenewDynamic.