为 lunr.js 添加中文支持

如果你能找到这里,那么你应该是知道 lunr.js 的。 Ok, 那 lunr.js 是什么?官方网站上说: “Simple full-text search in your browser”。翻译过来就是:浏览器上的简单的纯文本搜索工具。是的,它可以直接在浏览器上运行,不依赖服务端来完成对网页上纯文本的搜索功能,可以极大的提升通过网络服务器请求获取搜索结果时不流畅的搜索体验。用在博客等应用中,真是再好不过。然而,和大部分老外制作的工具一样,对于中文,老外的工具还是水土不服。对于中文网页搜索最大的问题就是,lunr.js 不支持中文搜索,由此还影响到一系列基于 lunr.js 制作的本地搜索工具,如 gitbook 内置的 gitbook-search-plugin 等,所以如果你在使用 gitbook 的搜索功能,发现输入中文出现错误结果不要大惊小怪。

不多说,以下基于当前 lunr.js 版本(commit id:6a978d1ebf1a0c08e1a76cd4f7f74ad5490e5937),添加了中文支持。

仓库地址:https://github.com/linfuyan/lunr.js

未分词的中文支持版本分词的中文支持版本,位于仓库中不同的分支。

未分词的中文支持版本

中文支持的基本原理是,保证 lunr.js 在分析过程中中文字符不会被转义。因此在 trimmer 函数中对中文字符做过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
lunr.trimmer = function (token) {
//by ming300 check token is chinese then not replace
if(isChineseChar(token)){
return token;
}
return token
.replace(/^\W+/, '')
.replace(/\W+$/, '')
}

/**
**check it contains Chinese (including Japanese and Korean)
*/
function isChineseChar(str){
var reg = /[\u4E00-\u9FA5\uF900-\uFA2D]/;
return reg.test(str);
}

此外,在分词部分 tokenizer 函数做些调整,添加如下关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var str = obj.toString().replace(/^\s+/, '')

for (var i = str.length - 1; i >= 0; i--) {
if (/\S/.test(str.charAt(i))) {
str = str.substring(0, i + 1)
break
}
}

var rs = str
.split(/[\ |\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\uFE30-\uFFA0|\(|\)|\-|\_|\+|\=|\||\\|\[|\]|\{|\}|\;|\:|\"|\'|\,|\<|\.|\>|\/|\?]+/)
.map(function (token) {
var t = token.replace(/[\ |\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\uFE30-\uFFA0|\(|\)|\-|\_|\+|\=|\||\\|\[|\]|\{|\}|\;|\:|\"|\'|\,|\<|\.|\>|\/|\?]/g, '').toLowerCase()
return t;
});

这里仅仅是基于英文中简单的通过空格进行分词的原则。因此会存在比较大的局限性。例如:”中文Chinese and English”,则直接被识别为”中文Chinese”、”and”、”English”,而无法将”中文”和”Chinese”再区分开。

相较于第二种方案的有点则是没有任何其他依赖,很轻量,适用于网页。而对基于网页标签进行的搜索则比较好,因为,标签本身就是简短,分散的。可以认为是分词好的。

基于分词的中文支持版本

这个版本基于node-segment中文分词库,在未分词版本的基础上引入分词。功能自然更强大,不过由于分词库的依赖,导致整个包太大。由于需要读取文件形式的分词库,无法在浏览器上使用,但可以在 nodejs 服务端用。虽然做些修改,可以将分词库作为 js 文件 require 进来,但还是存在包太大的问题。

关键代码的修改也是在 lunr.js 中的 trimmer 和 tokenizer 方法中。不过需要注意的是,在 package.json 中添加对 node-segment 的依赖。

感谢以下两个仓库作者。

https://github.com/ming300/lunr.js/

https://github.com/nandy007/lunr.js

以及以下文章:

我的建站路4:解决 lunr.js 的中文支持问题