惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
S
Security Affairs
Engineering at Meta
Engineering at Meta
MongoDB | Blog
MongoDB | Blog
Microsoft Security Blog
Microsoft Security Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
月光博客
月光博客
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
有赞技术团队
有赞技术团队
博客园 - 叶小钗
Hugging Face - Blog
Hugging Face - Blog
Microsoft Azure Blog
Microsoft Azure Blog
V
V2EX
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
腾讯CDC
C
Cisco Blogs
T
Tor Project blog
The Hacker News
The Hacker News
雷峰网
雷峰网
MyScale Blog
MyScale Blog
博客园 - 司徒正美
AWS News Blog
AWS News Blog
GbyAI
GbyAI
Y
Y Combinator Blog
D
DataBreaches.Net
Simon Willison's Weblog
Simon Willison's Weblog
S
Securelist
The GitHub Blog
The GitHub Blog
S
SegmentFault 最新的问题
T
Tenable Blog
L
LangChain Blog
M
MIT News - Artificial intelligence
N
Netflix TechBlog - Medium
The Cloudflare Blog
A
About on SuperTechFans
IT之家
IT之家
F
Fortinet All Blogs
Security Latest
Security Latest
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
NISL@THU
NISL@THU
爱范儿
爱范儿
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
W
WeLiveSecurity
A
Arctic Wolf
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog

晚晴幽草轩

生财有迹 | 您专属的资产跟踪与分析工具 如何将 Tailwind CSS 引入快应用开发提升编码效率? 关于「晚晴幽草轩」博客缓更说明 素问智聊斋,非官方 ChatGPT 在线客户端,免费可用 为什么说 WebAssembly 让 Web 的未来更光明? 近十年新兴行业技术,及简单介绍 如何基于 Prerender 为 SPA 做 SEO 优化 2022,「倾城之链」运营第 6 年感记 2022,如何优雅学习前端开发? 2022,值得学习系列之:函数式编程 地表最强 Markdown 文本编辑工具——Obsidian 如何制作 Google 自适应广告素材? 优质网站同好者周刊(第 50 期)- 倾城之链 关于“清风明月阁”的由来,以及未来 优质网站同好者周刊(第 49 期)- 倾城之链 优质网站同好者周刊(第 48 期)- 倾城之链 优质网站同好者周刊(第 47 期)- 倾城之链 优质网站同好者周刊(第 46 期)- 倾城之链 优质网站同好者周刊(第 45 期)- 倾城之链
如何打造体验良好的本地全文搜索?
晚晴幽草轩轩主 · 2022-03-21 · via 晚晴幽草轩

对于博客或者文档,全文搜索是非常重要的功能。现如今,网站生成器工具,无论是静态的、携带完整后台的、还是基于低代码、无代码的,皆不可胜数,诸如:HexoVuepressDocsifyHugoGhost 等,但之于「搜索」功能,或未提供、或不尽如人意;基于这样的背景,本文旨在探讨如何打造本地全文搜索

如何打造体验良好的本地全文搜索?

根据先前的些许经验,要使得本地全文检索,拥有良好体验,至少要包含以下几个方面:

  • 基于全文本内容,建好索引(数据),支持用户输入关键字,能快速、准确检索到结果;
  • (实时)展示检索结果列表,高亮检索关键字,最好可以按照匹配程度高低来排序;
  • 支持点击结果列表项跳转至页面,高亮所有关键字,并将页面滚动至第一个关键字位置;
  • 更多优化,支持用户分词搜搜,增加 -\* 等匹配方式(一般来讲,满足以上三条已足够);

下面,与大家分享下,如何通过简洁操作,做到以上三条:

如何检索关键字?

方案一

基于类 Gitbook 默认方案来创建索引。如快应用文档,其数据来源于:search_plus_index.json;在本地编译 md 文件时,将所有其内容纯文本化,作为数据源,并将其他信息如路径、标题、关键字等,一并存储于该 JSON 文件;发起关键字检索时,只需读取该 json 内容,并逐个遍历;将存有关键字的项 push 至结果数组,从而展示给用户就好。

创建索引

1

gitbook build

发起搜索

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

function query(keyword) {

if (keyword == null || keyword.trim() === '') return;

var results = [],

index = -1;

for (var page in INDEX_DATA) {

if ((index = INDEX_DATA[page].body.toLowerCase().indexOf(keyword.toLowerCase())) !== -1) {

results.push({

url: page,

title: INDEX_DATA[page].title,

body: INDEX_DATA[page].body.substr(Math.max(0, index - 50), MAX_DESCRIPTION_SIZE).replace(new RegExp('(' + escapeReg(keyword) + ')', 'gi'), '<span class="search-highlight-keyword">$1</span>')

});

}

}

displayResults({

count: results.length,

query: keyword,

results: results

});

}

方案二

基于 Lunr.js (A bit like Solr, but much smaller and not as bright.)结合 nodejieba(”结巴”中文分词的 Node.js 版本),来实现中文全文搜索。

Lunr.js is a small, full-text search library for use in the browser. It indexes JSON documents and provides a simple search interface for retrieving documents that best match text queries.

Lunr.js 是一个用于浏览器的小型全文搜索库。它索引 JSON 文档并提供一个简单的搜索界面,用于检索与文本查询最匹配的文档。它具有以下功能特征:

  • 简单的:Lunr 设计小巧但功能齐全,让您无需外部服务器端搜索服务即可提供出色的搜索体验。
  • 可扩展:添加强大的语言处理器为用户查询提供更准确的结果,或调整内置处理器以更好地适应您的内容
  • 到处:Lunr 没有外部依赖项,可以在您的浏览器或带有 node.js 的服务器上工作;

基本原理:Lunr 将字符串拆分为单词标记,然后经过一系列处理(score 计算分数、metadata 元数据),最终组装成为结果对象数组;关于这块儿详细信息,可参见 Lunr.js 核心概念;值得一提的是,Lunr 不能很好支持中文,因此对于中文分析,有借助 nodejieba 做了处理。具体创建索引的过程,参见如下代码(基于 Gatsbyjs 的实现):

创建索引

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

const marked = require('marked')

const striptags = require(`striptags`)

const lunr = require('lunr')

require('./src/helper/lib/lunr.stemmer.support.js')(lunr)

require('./src/helper/lib/lunr.zh.js')(lunr)

const createIndex = async (docsNodes, type, cache) => {

const cacheKey = `IndexLunr`

const cached = await cache.get(cacheKey)

if (cached) {

return cached

}

const documents = []

const store = {}

for (const node of docsNodes) {

const { slug } = node.fields

const title = node.frontmatter.title

const tag = node.frontmatter.tag

const content = striptags(marked.parse(node.rawMarkdownBody))

documents.push({

slug,

title,

tag,

content,

})

store[slug] = {

title,

tag,

content,

}

}

const index = lunr(function () {

this.use(lunr.zh)

this.ref('slug')

this.field('title')

this.field('tag')

this.field('content')

for (const doc of documents) {

this.add(doc)

}

})

const json = { index: index.toJSON(), store }

await cache.set(cacheKey, json)

return json

}

发起搜索

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

getQueryResult(query) {

const lunrData = this.props.lunrData

const { store } = lunrData.LunrIndex

const index = Index.load(lunrData.LunrIndex.index)

let results = []

try {

results = index.search(query).map(({ ref }) => {

return {

slug: ref,

...store[ref],

}

})

return results || []

} catch (error) {

console.error(`Something Error: ${error}`)

return results

}

}

handleSearch(keywords) {

let queryResultArr;

if (!keywords) {

queryResultArr = []

} else {

queryResultArr = this.getQueryResult(keywords)

}

this.keywords = keywords

this.setState({

queryResultArr: queryResultArr,

isShowResults: queryResultArr.length > 0

})

}

如何高亮文本?

1

2

3

4

5

6

7

8

highlightKeyword(keyword) {

const contentDom = document.querySelector(`#layout .wrapper .content`)

const instance = new Mark(contentDom);

instance.mark(keyword, {

exclude: ["h1"],

className: "mark-highlight"

});

}

如何定位到具体内容?

1

2

3

4

setTimeout(() => {

const markNode = document.querySelector("#layout .mark-highlight");

markNode && markNode.scrollIntoView({ behavior: "smooth", block: "start" });

}, 1000)

先前基于 Gatsbyjs 搭建一个静态网站,上文中提及的代码,完整版可参见:blog.nicelinks.site | search。在线示例:倾城周刊快应用消息中心

您可能感兴趣的文章