给全文搜索引擎Manticore (Sphinx) search 增长中文分词

文章首发于个人技术博客:你能够在上面看到更多的Python教程python爬虫html

Sphinx search 是一款很是棒的开源全文搜索引擎,它使用C++开发,索引和搜索的速度很是快,我使用sphinx的时间也有好多年了。最初使用的是coreseek,一个国人在sphinxsearch基础上添加了mmseg分词的搜索引擎,惋惜后来再也不更新,sphinxsearch的版本过低,bug也会出现;后来也使用最新的sphinxsearch,它能够支持几乎全部语言,经过其内置的ngram tokenizer对中文进行索引和搜索。python

可是,像中文、日文、韩文这种文字使用ngram仍是有很大弊端的:git

当Ngram=1时,中文(日文、韩文)被分解成一个个的单字,就像把英文分解成一个个字母那样。这会致使每一个单字的索引很长,搜索效率降低,同时搜索结果习惯性比较差。github

当Ngram=2或更大时,会产生不少无心义的“组合”,好比“的你”、“为什”等,致使索引的字典、索引文件等很是大,同时也影响搜索速度。算法

基于以上弊端,为中日韩文本加入分词的tokenizer是颇有必要的。编程

因而决定来作这件事。先去Sphinxsearch网站去看看,发现它已经发布了新的3.x版本,并且加入了不少很棒的特性,然而它从Sphinxsearch 3.x 开始,暂时再也不开源. 不过,部分前Sphinxsearch的开发人员跳出来成立新团队,在Sphinx 2.x版本基础上开发本身的Manticoresearch。这二者很像,从它们的名字就能够看出来,这俩都是狮身怪兽。数组

Sphinx 是(古埃及)狮身人面像,Manticore 是(传说中的)人头狮身龙(蝎)尾怪兽性能优化

Manticoresearch 从Sphinxsearch 继承而来, 并作了性能优化. 所以,我选择了Manticoresearch 来添加中日韩分词。架构

首先从Manticoresearch的github仓库pull最新的代码来谈价,后面我也会尽力与Manticoresearch的主分支保持同步。python爬虫

算法实现

算法基于字典,具体是cedar的实现的双数组trie。cedar是C++实现的高效双数组trie,也是分词字典的最佳之选。cedar的协议是GNU GPLv2, LGPLv2.1, and BSD;或者email联系做者所要其它协议。

经过最小匹配(而非单字)来匹配字典和字符串,把字符串分割成最短(而非单字)的词。若是遇处处理不了的歧义时,以单字作词。这样的目的是,保证搜索时能找到这些内容而不丢失。

稍微解释一下,对于搜索引擎的分词为何这么作:

  1.  搜索引擎要能找到尽量全内容:最完全的方法是ngram=1,每一个字单独索引,这样你搜索一个单字“榴”时,含有“榴莲”的文本会被找到,但缺点就如前面所说。
  2. 搜索引擎要能找到尽量相关的内容: 分词就是比较好的方法,对词进行索引,这样你搜索一个单字“榴”时,含有“榴莲”的文本就不会被找到。但分词的粒度要小,好比“编程语言”这是一个词组,若是把这个分红一个词,你搜索“编程”时,就找不到只含“编程语言”的文本,一样的,“上海市”要分红“上海”和“市”,等等。因此,“最小匹配”适用于搜索引擎。

编译安装

从github仓库manticoresearch-seg获取源码,编译方法跟Manticoresearch同样,具体看官方文档

使用方法

1. 准备词表 把全部词写到一个txt文件,一行一个词,以下所示:

# words.txt
中文
中国語
중국어

2. 建立字典 成功编译代码后,就会获得建立字典的可执行程序make_segdictionary. 而后执行命令:

./make_segdictionary words.txt words.dict

这样就获得了字典文件: words.dict

3. 配置索引 只需在配置文件的 index {...} 添加一行便可:

index {
    ...
    seg_dictionary = path-to-your-segmentation-words-dictionary
    ...
}

提醒: 分词对批量索引和实时索引都起做用。

 

吐槽

添加分词最初的想法是,个人代码做为新增文件加入项目,只在原有文件个别处添加就好。这样作分得比较清楚,后面对manticore官方仓库提交代码也比较清晰。因而就尝试这样作。

然而,Sphinx的代码组织的真是有点乱,Manticore沿用Sphinx的代码因此架构是同样的。最大的一个cpp文件sphinx.cpp 居然有3万多行代码,不少类的声明直接放在这个.cpp 文件里面,而没有放到头文件sphinx.h里面。 由于我实现的分词tokenizer必需要继承它的类保持接口一致。尝试着把cpp文件的一些声明移到.h文件,结果是越移越多,要对原始文件作很大改动,甚至可能要从新架构源代码。不是不能够从新架构,一来会很费时间,二来向官方提交代码很难被接受,三是跟官方代码保持同步就很费劲,最终仍是在原来sphinx.cpp文件中添加分词tokenizer: CSphTokenizer_UTF8Seg 。

固然,Sphinx的代码的类的继承关系比较清晰,继承原来的tokenizer实现新的也不算费事,修改了4个源码文件就添加好了分词tokenizer。

相关文章
相关标签/搜索