在 Hugo Blog 中加上 Algolia 文章搜索工具

Posted by Ian Tsai on Saturday, March 13, 2021

隨著文章越來越多,要找到以前寫的文章就越來越麻煩,所以就一直想尋找站內搜索的功能使用。之前有試用 google 的站內搜索功能,但總覺得整體不是很好看,也就沒有繼續使用。直到最近看到 Algolia ,可建立文章的索引,將資料上傳到 Algolia,再藉由串接 API 就可以完成搜索功能。

接下來就開始建立站內搜索功能吧。

註冊 Algolia


第一步就是要到 Algolia 註冊帳號,登入後會要求輸入一個 index(等等要用到這個 index),然後會以這個 index 建立一個 app ,接著選擇存取的國家(我是選日本),完成之後會有這個畫面。

app

然後點選左邊的 API Keys,這邊有等等設定需要使用的參數,可以先將他們記下來。

API Keys

註:Algolia 有分幾個付費等級,現在有 15 天試用期,不過因為 blog 只需要簡單的搜索文章的功能,這裡目前是使用免費版,若要更進階的功能可以自行研究看看。

hugo 生成索引文件


1. 修改 config.toml

修改./config.toml設定好需要的參數。

[outputs]
  home = ["HTML", "RSS", "Algolia"]
  
[outputFormats.Algolia]
  baseName = "algolia"
  isPlainText = true
  mediaType = "application/json"
  notAlternative = true
  
[params.algolia]
  appId = "剛剛看到的 ID"
  indexName = "一開始輸入的 index"
  searchOnlyKey = "剛剛看到的 Search Only Key"
  vars = ["title", "summary", "date", "publishdate", "expirydate", "permalink"] 
  params = ["categories", "tags", "series"]
2. 新增 list.algolia.json

layouts/_default路徑下新增list.algolia.json文件,並加入以下程式碼,可以依照每個不同的 blog 所含有的參數去做設定。

{{- $.Scratch.Add "index" slice -}}
{{- $section := $.Site.GetPage "section" .Section}}
{{- range .Site.AllPages -}}
  {{- if or (and (.IsDescendant $section) (and (not .Draft) (not .Params.private))) $section.IsHome -}}
    {{- $.Scratch.Add "index" (dict "objectID" .UniqueID "date" .Date.UTC.Unix "description" .Description "dir" .Dir "expirydate" .ExpiryDate.UTC.Unix "fuzzywordcount" .FuzzyWordCount "keywords" .Keywords "kind" .Kind "lang" .Lang "lastmod" .Lastmod.UTC.Unix "permalink" .Permalink "publishdate" .PublishDate "readingtime" .ReadingTime "relpermalink" .RelPermalink "summary" .Summary "title" .Title "type" .Type "url" .URL "weight" .Weight "wordcount" .WordCount "section" .Section "tags" .Params.Tags "categories" .Params.Categories "authors" .Params.Authors)}}
  {{- end -}}
{{- end -}}
{{- $.Scratch.Get "index" | jsonify -}}
3. 產生 algolia.json

之後在執行hugo建立的public時可以確認一下有沒有產生algolia.json的索引文件。

自動上傳索引至 Algolia


1. 安装 atomic-algolia
npm init
npm install atomic-algolia --save

註:這裡需要用到 npm 套件工具,所以需要安裝 node js,若沒有安裝的可以到 這裏下載。

2. 修改根目錄下的 package.json

到根目錄下的 package.json 內,在 scripts 下添加 “algolia”: “atomic-algolia”

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "algolia": "atomic-algolia"
  },
3. 建立 .ENV 檔
ALGOLIA_APP_ID=你的Application ID
ALGOLIA_INDEX_NAME=你的索引名字
ALGOLIA_INDEX_FILE=public/algolia.json
ALGOLIA_ADMIN_KEY=你的Admin API Key
4. 上傳索引文件

可以在 local 端執行npm run algolia這行指令查看效果:

run algolia

5. 設定索引

回到 Algolia 做索引的條件設定,勾選自己想要的所以關鍵字,之後便會依照這些關鍵字給予搜索結果。

setting

6. 測試索引

可以測試看看搜索功能是否成功,若輸入關鍵字,後方有出現結果,就是設定成功,可以搜尋了。

search

畫面添加搜尋欄


註:這部分的 html, css 都使用現成的,只稍作修改。

1. 新增 search.html

layouy/partials/ 路徑下新增一個

 1<div class="aa-input-container" id="aa-input-container">
 2  <input type="search" id="aa-search-input" class="aa-input-search" placeholder="Search for titles or URIs..."
 3    name="search" autocomplete="off" />
 4  <svg class="aa-input-icon" viewBox="654 -372 1664 1664">
 5    <path
 6      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" />
 7  </svg>
 8</div>
 9<script src="https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/algoliasearch.min.js"></script>
10<script src="https://res.cloudinary.com/jimmysong/raw/upload/rootsongjc-hugo/autocomplete.min.js"></script>
11<script>
12  var client = algoliasearch({{.Site.Params.algolia.appId}}, {{.Site.Params.algolia.searchOnlyKey}});
13  var index = client.initIndex({{.Site.Params.algolia.indexName}});
14  autocomplete('#aa-search-input',
15    { hint: false }, {
16    source: autocomplete.sources.hits(index, { hitsPerPage: 20 }),
17    displayKey: 'name',
18    templates: {
19        suggestion: function (suggestion) {
20            return '<span>' + '<a href="' + suggestion.relpermalink + '">' +
21            suggestion.title + '</a></span>';
22      }
23    }
24  });
25
26  $('#searchGlyph').click(function() {
27    setTimeout(function(){
28      $('#aa-search-input').focus();
29    },300);
30  });
31</script>

幾點注意事項:

  1. algoliasearch 以及 client.initIndex 所需要的參數在最一開始我們就已經設定到config.toml內了,如果還沒設定的可以往前看。

  2. suggestioncall api 後回傳的物件,若想知道裡面有什麼,可以用console.log()將其印出來,並確認templates的 return 是否有使用到物件內的欄位。

  3. 有些教學文會使用docsearch這個 function,但在這個 function 我測試過是無法使用,查了一些資料發現比較推薦使用autocomplete這個 function,所以大家在使用時可以注意這一點。

  4. 為了方便點選 search icon 之後可以直接輸入查詢資料,所以要增加 26-30 行的程式碼,而因為頁面 modal 內的 input 渲染較慢,所以需要設定延遲的時間 ,才有辦法 focus 到 input 內。

2. 在 header 加入搜索按鈕

先在 header 最後加上放大鏡的 icon

<a href="#modalSearch" data-toggle="modal" data-target="#modalSearch" style="outline: none;">
    <span id="searchGlyph" class="glyphicon glyphicon-search"><i class="fas fa-search"></i></span>
</a>

接著加入搜所匡


<div id="modalSearch" class="modal fade" role="dialog">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title">文章搜尋</h4><button type="button" class="close" data-dismiss="modal">&times;</button>
      </div>
      <div class="modal-body">
        {{ partial "search.html" . }}
      </div>
      <!-- <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">close</button>
      </div> -->
    </div>
  </div>
</div>
3. 加入 css

可以到staic/css內新增search.css,但我這部分設定好像沒弄好,所以我是新增在assets/scss/main.scss內,建議如果要加在這的話,可以寫幾行註解,以免日後不知道自己新增哪些 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-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;
}

這一步如果剛剛是建立一個新的 search.css的話,要到 layout/partails/head.html 內將剛剛寫好的 css 引入。若是在main.scss內則不用。

  <link rel="stylesheet" href="{{ " css/search.css" | absURL }}" />

到這邊基本設定都已經完成,接著就可以試用看看自己的文章搜索功能拉!!

search block

如果無法使用或有更好的建議都歡迎留言給我,感謝收看!!

References


  • Hugo 集成 Algolia 搜索
  • Hugo添加Algolia搜索支持
  • Static site search with Hugo + Algolia