flexsearch 是一个非常优秀的全文搜索库,本文参考 respress 源码实现,将文章全文索引到本地,并使用 flexsearch 进行全文搜索。
在 NextJs 项目中,新建一个 search.mjs 的文件。文件中调用数据接口,json
字符转换为Uint8Array
。
使用WebAssembly
模块,加载wasm
文件。将Uint8Array
数据传入wasm
暴露出的加密函数中。
加密数据
按照rspress
的搜索方案,全文的数据全部存储在了 search-index.json
中,也就是用户可以简单的下载 json 文件,就能拿到全部数据。
为了避免访客可以拿到简单的拿到全部数据,我们使用加密方案,将数据加密后,再返回给用户。当然这还是能破解的,只是需要一定的门槛。
因为最近在学习代码安全,涉及到了WebAssembly
, 因此最终决定使用rust
开发加密方案。最终通过构建工具打包成wasm
,文件。
主要有俩个函数encrypt
和decrypt
。
encrypt
接收Uint8Array
数据,
进行简单的加密算法运算
压缩输出索引数据
使用flate2
将上一步输出的数据进行压缩 xml 格式,再压缩输出Uint8Array
数据。
2、3 详细代码,示例为search-transfor.wasm
文件
Node 将数据写入到本地。
这个没什么好讲的,直接使用fs
写入即可。示例文件叫search-[hash].ahri
, 后缀名自定义即可。hash 可以根据数据内容生成,区分版本。
提前生成索引
2024/11/01 更新。
实际操作中,发现数据量级较大时候,flexsearch
生成索引的时间非常长。幸好支持nodejs, 因此放弃了直接存储原始数据,选择在项目构建的时候提前生成索引,直接存储索引文件,减少flexsearch
初始化时间。
执行createFlexDocument
,最后把index数据使用fs存储到search-[hash].ahri
即可。
构建时机,因为要使用hash保证唯一性,所以构建的时间选择放到
next.config.mjs
中,构建完成后再执行next build
, 将Hash使用definePlugin 插件,注入到全局变量中。本文所有hash都是指该全局变量
search-transfor.wasm
文件和索引文件search-[hash].ahri
, 以下代码还包含了idb 缓存逻辑,具体说明会放到数据缓存模块说明。上一步的数据是缓存的索引,因此不需要重建索引,只需要导入索引即可。代码直接复用生成索引的代码,传入cache 即可。
3. 具体搜索逻辑
查看拓展web-worker
这块重要的逻辑就是计算索引的位置,取出搜索关键词前后的内容,标题等。其他的就是正常输入搜索,展示搜索结果的react组件,想了解具体代码的逻辑,可以参考Rspress源码, 不在详细赘述
除了第一步中的索引缓存, 考虑到wasm
和search.[hash].ahri
文件 体积很大,而且基本是静态数据,因此绝对缓存到indexDB 中,减少网络传输耗时。
参考客户端获取数据的代码。
做了提前生成索引以及数据缓存之后,整体的搜索模块初始化时间由80s 降低到了2s左右(非首次)
做这一块缓存除了用户搜索体验外,还为了节省cdn流量。每次加载文件的话需要消耗30M +
相关,所以简单的将加解密的逻辑使用rust
编写,然后编译成wasm
文件。exprot
加解密函数。
实际操作中索引的量级过高(压缩后还有 30M+), 导致每次初始化索引(90s 左右)的时候整个输入框会卡住,不响应用户输入。 最终决定搜索逻辑放到web-worker
中。
nextjs 默认支持 web-worker,所以直接使用即可。可以参考with-web-worker