Siven's Corner

给 Hugo 添加 Pagefind 全文搜索

一。前言

一直想给博客添加一个文章内容搜索的功能,之前看到有用 Fuse.js 和 Algolia 来实现的,浅浅尝试了这两种发现都不是很符合自己的需求,并且折腾起来麻烦,所以一顿搜索,偶然间发现了 Pagefind,配置简单同时功能完美契合自己的需求。

二。数据准备

1.首先进入到 Hugo 博客项目的根目录,安装 Pagefind,自己使用 npm 来管理包的,所以对应的命令是 npm install -D pagefind

2.输入命令 hugo,生成静态页面到 public 目录下,做好生成全文索引前的数据准备工作。

3.生成索引,在当前目录下,准备一个 pagefine.yml 配置文件。然后执行 npx pagefind命令,这时会根据配置来生成索数据。当然也可以不用配置文件,在刚刚命令后面指定对应参数,同样可以达到目的。

# pagefind.ygml
source: public
glob: "{p/*/index.html,card/*/index.html}"  # 需要索引的目录
indexing:
  language: "zh"  # 强制中文分词
  heading_behavior: all_text  # 关键设置:标题全文索引(非仅前缀)
  term_split: 12  # 提高中文分词粒度

4.执行完上诉命令后,会在 public 目录下面,看到一个 pagefind 文件夹,里面就是刚刚生成的搜索索引的数据,到这里前期的数据基本准备好了。

三。搜索展示

搜索功能,我是把它集成到现有的 nav 导航栏上,也就是多加一个菜单,点击跳转到搜索页面来检索信息。

1.准备导航菜单项目,在 content 目录下,新建一个 search 文件夹,里面准备一个 _index.md 文件。

+++
title = "Search"
menu = "main"
weight = 6
+++

2.布局文件准备,在 layouts 下面,新建 search 文件夹,里面准备一个 list.html 文件,这里就是生成搜索框和结果的地方。

{{ define "main" }}
<div style="margin-top: 15px;"></div>
<div id="search">
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
<script src="/pagefind/pagefind-ui.js" type="module"></script>

<script>
    window.addEventListener('DOMContentLoaded', (event) => {
        new PagefindUI({ 
            element: "#search", 
            showSubResults: false, 
            showImages: false,
            showFilters: true,
            filters: ["tag", "type"]
        });
    });
</script>
{{ end }}

3.在第二步里面,引用了 pagefind-ui.css 和 pagefind-ui.js,它们可以在第一步生成的 pagemfind 文件里面找到,把他们放到 static/pagefind/ 目录下。

4.进入搜索页面验证下查询效果,到这里基础的功能基本上实现了。

四。遇到的问题

1.开始生成标题的问题,默认把 header.html 里面的标题也放进索引,导致搜索时所有文章的标题都是这个,因为他比文章的标题级别高,优先展示它。所以这里用 data-pagefind-ignore 来达到忽略的效果;同样对于 _default/single.html 的文章标题,增加 data-pagefind-meta,能够被索引文件显式记录。

# header.html
<a href="{{ "" | relURL }}" class="title">
  <h1 data-pagefind-ignore>{{ .Site.Title }}</h1>
</a>
# single.html
<h1 data-pagefind-meta="title">{{ .Title }}</h1>

2.由于博客是中英文都存在的情况,其中中文占大部分,所以在 2.3 节配置里面,指明了语言,分词颗粒度也做了调整。同样在 hugo 生成的 index.html 里面,也要注意下语言问题,文件开头<html lang="zh">标签。

3.增加 filter 功能,对现有搜索的增强,这里绕了点弯路,最终还是 Claude 3.7 帮忙解决。这里需要调整 layouts/_default/baseof.html。我增加了标签和文章类型过滤,标签是从 md 文件里面直接提取,如果想用分类也是类似的方法;第二个是文章类型,目前我有 blog 和 card 两个类型,一个是长篇一点,另一个是类似卡片笔记一样。

<!DOCTYPE html>
<html lang="{{ with .Site.LanguageCode }}{{ . }}{{ else }}en-US{{ end }}">

<head>
  <meta http-equiv="X-Clacks-Overhead" content="GNU Terry Pratchett" />
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  {{- partial "favicon.html" . -}}
  <title>{{- block "title" . }}{{ with .Title }}{{ . }} | {{ end }}{{ .Site.Title }}{{- end }}</title>
  <meta name="referrer" content="no-referrer-when-downgrade" />

  {{ with .OutputFormats.Get "rss" -}}
  {{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }}
  {{ end -}}

  {{- partial "style.html" . -}}

  <!-- A partial to be overwritten by the user.
  Simply place a custom_head.html into
  your local /layouts/partials-directory -->
  {{- partial "custom_head.html" . -}}
</head>

<!-- 调整点1: data-pagefind-body -->
<body data-pagefind-body>

  <!-- 调整点2:添加标签过滤器 -->
  {{ with .Params.tags }}
    {{ range . }}
      <meta data-pagefind-filter="tag:{{ . }}" />
    {{ end }}
  {{ end }}

  <!-- 调整点3:添加文章类型过滤器 -->
  {{ if eq .Type "card" }}
    <meta data-pagefind-filter="type:card" />
  {{ else if eq .Type "blog" }}
    <meta data-pagefind-filter="type:blog" />
  {{ else }}
    <meta data-pagefind-filter="type:blog" />
  {{ end }}

  <header>
    {{- partial "header.html" . -}}
  </header>
  <main>
    {{- block "main" . }}{{- end }}
  </main>
  <footer>
    {{- partial "footer.html" . -}}
  </footer>

  <!-- A partial to be overwritten by the user.
  Simply place a custom_body.html into
  your local /layouts/partials-directory -->
  {{- partial "custom_body.html" . -}}
</body>

</html>

五。总结

前前后后花了两天时间,中途绕了点弯路,不过结果很满意,所以在此记录下。

#Notes #Hugo