各种bug的解决方法

mkdocs如何支持中文搜索

2017-08-31  本文已影响280人  craoy

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如何实现全文搜索的:

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

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 = 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]\.";
上一篇 下一篇

猜你喜欢

热点阅读