mkdocs如何支持中文搜索

mkdocs是一个很方便的文档网站生成器,文档使用Markdown格式来编写,luat的wiki就是用mkdocs生成的。
mkdocs自带搜索功能,可惜的是不支持中文搜索,同事参考这个方案https://my.oschina.net/u/3465063/blog/895142 实现了中文搜索的功能,但是大家用了发现搜索很慢(搜索时间在8秒左右)。

下面记录一下解决这个问题的过程:

分析问题

1.用chrome的开发者工具分析了一下,发现大部分时间都在执行加载文档(search.js: index.add(doc))的接口上,粗看了一下这部分js代码没看出什么异常,只好先了解mkdocs如何实现搜索的

2.分析mkdocs如何实现全文搜索的:

  • 在mkdocs build构建站点时,生成全部文档的JSON格式的数据(search_index.json),再使用lunr.js提供文本搜索功能,在生成的站点目录下会有如下数据:
    site/mkdocs/search_index.json: 全部文档的文本JSON数据
    site/mkdocs/js: lunr.js实现搜索功能需要的js脚本,这些js脚本是由mkdocs工具自带的
  • lunr.js介绍:Lunr.js是一个用于浏览器的小型全文搜索库。 它对JSON文档进行索引,并提供一个简单的搜索接口,用于检索最符合文本查询的文档

3.分析lunr的搜索的实现方式,可以简单总结为下面两个步骤:

  • 分词:在调用lunr的add接口添加文档数据时会在lunr内部由tokenizer接口对文档实现分词生成词库,举例说明一下什么是分词:
    比如有句话:我来到北京清华大学
    分词以后可以得到这些词: 我,来到,北京,清华,华大,大学
  • 匹配:将要搜索的文本与每个文档的分词库匹配,如果匹配成功,就将该文档输出到搜索结果中

4.此时再结合第一步的情况,可以发现之前的中文搜索方案分词速度很慢,所以执行时间很长

解决方案

经过上面的分析以后,现在知道解决这个问题的关键就是如何提高分词的效率,也可以说是如何快速得到文档的词库,那么有几个方案:

  1. 优化js代码,提高分词效率
  2. 直接在构建文档站点时候生成词库,浏览器直接加载词库,不再进行浏览器执行js分词

由于对分词算法不了解、js水平一般般,所以选择了第2个方案,那么就要找一个合适的分词库来完成中文分词,最后选择了结巴分词

方案确定以后,现在开始改造mkdocs,如果之前对mkdocs做过修改,请重新安装mkdocs将mkdocs恢复成原始文件:

  1. python安装jieba库
pip install jieba
  1. 修改search.py(lib\site-packages\mkdocs): 在generate_search_index生成index数据时调用jieba分词生成索引,jieba.cut接口第二个参数为true为全模式分词,没有采用搜索模式分词是觉得那个切词切的太碎了,感觉没有必要
    def generate_search_index(self):
        """python to json conversion"""
        page_dicts = {
            'docs': self._entries,
        }
        for doc in page_dicts['docs']:
            # 调用jieba的cut接口生成分词库,过滤重复词,过滤空格
            tokens = list(set([token.lower() for token in jieba.cut_for_search(doc['title'].replace('\n', ''), True)]))
            if '' in tokens:
                tokens.remove('')
            doc['title_tokens'] = tokens

            tokens = list(set([token.lower() for token in jieba.cut_for_search(doc['text'].replace('\n', ''), True)]))
            if '' in tokens:
                tokens.remove('')
            doc['text_tokens'] = tokens
  1. 修改lunr.js(lib\site-packages\mkdocs\assets\search\mkdocs\js\lunr.min.js, 压缩过的lunr.js文件 lunr.min.js)有两个地方要修改:
  • lunr.Index.prototype.add: 获取分词数据的方式,不在从内部的分词接口计算分词,直接从文档的词库加载
  • lunr.trimmer: 过滤空白字符的接口,修改匹配方式,把原来只匹配字母、数字改成匹配所有非空字符
lunr.Index.prototype.add = function (doc, emitEvent) {
  var docTokens = {},
      allDocumentTokens = new lunr.SortedSet,
      docRef = doc[this._ref],
      emitEvent = emitEvent === undefined ? true : emitEvent

  this._fields.forEach(function (field) {
    // 删掉内部接口计算分词
    // var fieldTokens = this.pipeline.run(this.tokenizerFn(doc[field.name]))
    // 直接从文档词库加载
    var fieldTokens = doc[field.name + '_tokens']

    docTokens[field.name] = fieldTokens

    for (var i = 0; i < fieldTokens.length; i++) {
      var token = fieldTokens[i]
      allDocumentTokens.add(token)
      this.corpusTokens.add(token)
    }
  }, this)
lunr.trimmer = function (token) {
  var result = token.replace(/^\s+/, '')
                    .replace(/\s+$/, '')  //  \W -> \s
  return result === '' ? undefined : result
}

现在重新构建发布站点(mkdocs build),可以发现搜索速度大大加快了。

注意:修改后的search_index.json会比之前大不少,我们从之前的300多K,变成了900多K,如果采用nginx作为http服务器的话,可以开启gzip压缩提高加载速度,gzip的压缩配置如下:

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/json;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,737评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,103评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,710评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,909评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,794评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,557评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,939评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,572评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,852评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,871评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,692评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,490评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,939评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,114评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,409评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,971评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,176评论 2 339

推荐阅读更多精彩内容