From 93cb788e52c39dc3e4e6e0fef8e4409ba3505d5d Mon Sep 17 00:00:00 2001 From: Xin Date: Sat, 21 Oct 2023 16:00:39 -0400 Subject: [PATCH] feat(search): support different search index types (#145) * add support for different search index types: `content | summary | heading | title` * resolves #139 --- assets/json/search-data.json | 9 +++- .../content/docs/guide/configuration.md | 31 +++++++++++ exampleSite/hugo.yaml | 7 +++ layouts/partials/scripts.html | 22 +++++--- layouts/partials/utils/fragments.html | 53 ++++++++++++------- 5 files changed, 95 insertions(+), 27 deletions(-) diff --git a/assets/json/search-data.json b/assets/json/search-data.json index 16d3ad7b..23e94ac2 100644 --- a/assets/json/search-data.json +++ b/assets/json/search-data.json @@ -1,3 +1,10 @@ +{{/* FlexSearch Index Data */}} +{{- $indexType := site.Params.search.flexsearch.index | default "content" -}} + +{{- if not (in (slice "content" "summary" "heading" "title" ) $indexType) -}} + {{- errorf "unknown flexsearch index type: %s" $indexType -}} +{{- end -}} + {{- $pages := where .Site.Pages "Kind" "in" (slice "page" "section") -}} {{- $pages = where $pages "Params.excludeSearch" "!=" true -}} {{- $pages = where $pages "Content" "!=" "" -}} @@ -7,7 +14,7 @@ {{- range $index, $page := $pages -}} {{- $pageTitle := $page.LinkTitle | default $page.File.BaseFileName -}} {{- $pageLink := $page.RelPermalink -}} - {{- $data := partial "utils/fragments" $page -}} + {{- $data := partial "utils/fragments" (dict "context" $page "type" $indexType) -}} {{- $output = $output | merge (dict $pageLink (dict "title" $pageTitle "data" $data)) -}} {{- end -}} diff --git a/exampleSite/content/docs/guide/configuration.md b/exampleSite/content/docs/guide/configuration.md index bf1e2488..2189887f 100644 --- a/exampleSite/content/docs/guide/configuration.md +++ b/exampleSite/content/docs/guide/configuration.md @@ -198,6 +198,37 @@ By default, the page width is set to `normal`. Similarly, the width of the navbar and footer can be customized by the `params.navbar.width` and `params.footer.width` parameters. +### Search Index + +Full-text search powered by [FlexSearch](https://github.com/nextapps-de/flexsearch) is enabled by default. +To customize the search index, set the `params.search.flexsearch.index` parameter in the config file: + +```yaml {filename="hugo.yaml"} +params: + # Search + search: + enable: true + type: flexsearch + + flexsearch: + # index page by: content | summary | heading | title + index: content +``` + +available options for `flexsearch.index`: +- `content` - full content of the page (default) +- `summary` - summary of the page, see [Hugo Content Summaries](https://gohugo.io/content-management/summaries/) for more details +- `heading` - level 1 and level 2 headings +- `title` - only include the page title + +To exclude a page from the search index, set the `excludeSearch: true` in the front matter of the page: + +```yaml {filename="content/docs/guide/configuration.md"} +--- +title: Configuration +excludeSearch: true +--- +``` ### Google Analytics diff --git a/exampleSite/hugo.yaml b/exampleSite/hugo.yaml index 149bff2d..1c5881b7 100644 --- a/exampleSite/hugo.yaml +++ b/exampleSite/hugo.yaml @@ -117,8 +117,15 @@ params: displayUpdatedDate: true dateFormat: "January 2, 2006" + # Search + # flexsearch is enabled by default search: enable: true + type: flexsearch + + flexsearch: + # index page by: content | summary | heading | title + index: content editURL: enable: true diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html index 1d567b11..15b079d4 100644 --- a/layouts/partials/scripts.html +++ b/layouts/partials/scripts.html @@ -13,16 +13,22 @@ {{- end -}} -{{/* FlexSearch */}} + +{{/* Search */}} {{- if (site.Params.search.enable | default true) -}} - {{- $jsSearchScript := printf "%s.search.js" .Language.Lang -}} - {{- $jsSearch := resources.Get "js/flexsearch.js" | resources.ExecuteAsTemplate $jsSearchScript . -}} - {{- if hugo.IsProduction -}} - {{- $jsSearch = $jsSearch | minify | fingerprint -}} + {{- $searchType := site.Params.search.type | default "flexsearch" -}} + {{- if eq $searchType "flexsearch" -}} + {{- $jsSearchScript := printf "%s.search.js" .Language.Lang -}} + {{- $jsSearch := resources.Get "js/flexsearch.js" | resources.ExecuteAsTemplate $jsSearchScript . -}} + {{- if hugo.IsProduction -}} + {{- $jsSearch = $jsSearch | minify | fingerprint -}} + {{- end -}} + {{- $flexSearchJS := resources.Get "lib/flexsearch/flexsearch.bundle.min.js" | fingerprint -}} + + + {{- else -}} + {{- warnf `search type "%s" is not supported` $searchType -}} {{- end -}} - {{- $flexSearchJS := resources.Get "lib/flexsearch/flexsearch.bundle.min.js" | fingerprint -}} - - {{- end -}} {{/* Mermaid */}} diff --git a/layouts/partials/utils/fragments.html b/layouts/partials/utils/fragments.html index 5b74bed2..0ab77f8c 100644 --- a/layouts/partials/utils/fragments.html +++ b/layouts/partials/utils/fragments.html @@ -1,5 +1,6 @@ {{/* Split page raw content into fragments */}} -{{ $page := . }} +{{ $page := .context }} +{{ $type := .type | default "content" }} {{ $headingKeys := slice }} {{ $headingTitles := slice }} @@ -22,24 +23,40 @@ {{ $len := len $headingKeys }} {{ $data := dict }} -{{ if eq $len 0 }} - {{ $data = $data | merge (dict "" $page.Plain) }} -{{ else }} - {{ range seq $len }} - {{ $i := sub $len . }} - {{ $headingKey := index $headingKeys $i }} - {{ $headingTitle := index $headingTitles $i }} - - {{ if eq $i 0 }} - {{ $data = $data | merge (dict $headingKey ($content | markdownify | plainify)) }} - {{ else }} - {{ $parts := split $content (printf "\n%s\n" $headingTitle) }} - {{ $lastPart := index $parts (sub (len $parts) 1) }} - - {{ $data = $data | merge (dict $headingKey ($lastPart | markdownify | plainify)) }} - {{ $content = strings.TrimSuffix $lastPart $content }} - {{ $content = strings.TrimSuffix (printf "\n%s\n" $headingTitle) $content }} +{{ if eq $type "content" }} + {{/* Include full content of the page */}} + {{ if eq $len 0 }} + {{ $data = $data | merge (dict "" ($page.Plain | htmlUnescape | chomp)) }} + {{ else }} + {{/* Split the raw content from bottom to top */}} + {{ range seq $len }} + {{ $i := sub $len . }} + {{ $headingKey := index $headingKeys $i }} + {{ $headingTitle := index $headingTitles $i }} + + {{ if eq $i 0 }} + {{ $data = $data | merge (dict $headingKey ($content | markdownify | plainify | htmlUnescape | chomp)) }} + {{ else }} + {{ $parts := split $content (printf "\n%s\n" $headingTitle) }} + {{ $lastPart := index $parts (sub (len $parts) 1) }} + + {{ $data = $data | merge (dict $headingKey ($lastPart | markdownify | plainify | htmlUnescape | chomp)) }} + {{ $content = strings.TrimSuffix $lastPart $content }} + {{ $content = strings.TrimSuffix (printf "\n%s\n" $headingTitle) $content }} + {{ end }} {{ end }} {{ end }} +{{ else if (eq $type "heading" ) }} + {{/* Put heading keys with empty content to the data object */}} + {{ $data = dict "" "" }} + {{ range $headingKeys }} + {{ $data = $data | merge (dict . "") }} + {{ end }} +{{ else if (eq $type "title") }} + {{/* Use empty data object since title is included in search-data.json */}} + {{ $data = $data | merge (dict "" "") }} +{{ else if (eq $type "summary" ) }} + {{ $data = $data | merge (dict "" ($page.Summary | plainify | htmlUnescape | chomp)) }} {{ end }} + {{ return $data }}