概述java
环境准备python
认识中文分词器nginx
彩蛋git
上一篇博文记录了elasticsearch插件安装和管理, 在地大物博的祖国使用es,不得不考虑中文分词器,es内置的分词器对中文分词的支持用惨不忍睹来形容不为过,看这篇博文以前,建议先看一下博文elasticsearch分词器,对分词器有个初步理解。本文将记录一下项目中如何使用选用和使用中文分词器的,但愿可以帮助到即未来踩坑的小伙伴们,欢迎批评指正github
本文都是基于elasticsearch安装教程 中的elasticsearch安装目录(/opt/environment/elasticsearch-6.4.0)为范例web
在博文elasticsearch分词器中提到elasticsearch可以快速的经过搜索词检索出对应的文章归功于倒排索引,下面经过中文举例看看倒排索引。数据库
中文分词器是作什么的呢? what? 经过名字就知道了啊,为何还要问。。。下面经过三个文档示例,看看它是如何分词的vim
文档1: 我爱伟大的祖国centos
文档2: 祝福祖国强大繁盛缓存
文档3: 我爱蓝天白云
通过中文分词器,以上文档均会根据分词规则,将文档进行分词后的结果以下:
注意:不一样的分词规则,分词结果不同,选择根据分词器提供的分词规则找到适合的分词规则
文档1分词结果: [我,爱,伟大,的,祖国]
文档2分词结果: [祝福,祖国,强大,繁盛]
文档3分词结果: [我,爱,蓝天白云,蓝天,白云]
经过上面的分词结果,发现拆分的每一个词都是咱们熟知的词语, 可是若是不使用中文分词,就会发现上面的文档把每一个字拆分红了一个词,对咱们中文检索很不友好。
看到上面中文分词器结果,就会有新的疑问,使用中文分词器那样分词效果有什么好处呢? 答案就是根据分词创建词汇与文档关系的倒排索引。这步都是es帮咱们作的,下面经过"我","爱","祖国"三个词看看倒排索引,以下图:
经过上图中的倒排索引,咱们搜索"祖国"时,es经过倒排索引能够快速的检索出文档1和文档3。若是没有中文分词器,搜索"祖国"就会被拆分"祖""国"两个词的倒排索引, 就会把包含"祖"的文档都检索出来,很明显就会和咱们想要的结果截然不同。
Smart Chinese Analysis: 官方提供的中文分词器,
IKAnalyzer: 免费开源的java分词器,目前比较流行的中文分词器之一,简单,稳定,想要特别好的效果,须要自行维护词库,支持自定义词典
结巴分词: 开源的python分词器,github有对应的java版本,有自行识别新词的功能,支持自定义词典
Ansj中文分词: 基于n-Gram+CRF+HMM的中文分词的java实现,免费开源,支持应用天然语言处理
hanlp: 免费开源,国人天然处理语言牛人无私风险的
我的对以上分词器进行了一个粗略对比,以下图:
截止到目前为止,他们的分词准确性从高到低依次是:
hanlp> ansj >结巴>IK>Smart Chinese Analysis
结合准确性来看,选用中文分词器基于如下考虑:
官方的Smart Chinese Analysis直接能够不考虑了
对搜索要求不高的建议选用 IK 学习成本低,使用教程多,还支持远程词典
对新词识别要求高的选用结巴分词
Ansj和hanlp均基于天然处理语言,分词准确度高,活跃度来说hanlp略胜一筹
博主选用的hanlp分词器,目前线上运行结果来看准确性知足需求
下面就写一下博主对IKAnalyzer 和 hanlp分词器的使用
截止目前,IK分词器插件的优点是支持自定义热更新远程词典。
es插件安装教程参考这里
ik的es插件地址: https://github.com/medcl/elasticsearch-analysis-ik/releases
博主使用的es版本是6.4.0,下载时要注意对应es版本
在线安装ik es插件 命令:
# /opt/apps/elasticsearch-6.4.0/bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.0/elasticsearch-analysis-ik-6.4.0.zip
查看插件安装列表
# sudo /opt/apps/elasticsearch-6.4.0/bin/elasticsearch-plugin list
ik安装完毕后配置文件在 {ES_HOME}/config目录下, 本例目录是 /opt/apps/elasticsearch-6.4.0/config/analysis-ik/IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <!--配置文件名称,代表整个配置文件的目的便可,保持默认挺好的 --> <comment>IK Analyzer 扩展配置</comment> <!--用户能够在这里配置本身的扩展字典,使用相对路径,多个词典使用逗号分隔,好比:custom/mydict1.dic,custom/mydict2.dic --> <entry key="ext_dict"></entry> <!--用户能够在这里配置本身的扩展中止词字典,使用相对路径,多个词典使用逗号分隔,好比:custom/mydict1.dic,custom/mydict2.dic --> <entry key="ext_stopwords"></entry> <!--用户能够在这里配置远程扩展字典,配置远程扩展字典,多个词典使用逗号分隔,好比: http://xxx.xx.com/xxx --> <entry key="remote_ext_dict">words_location</entry> <!--用户能够在这里配置远程扩展中止词字典,多个词典使用逗号分隔,好比: http://xxx.xx.com/xxx --> <entry key="remote_ext_stopwords">words_location</entry> </properties>
ik文本词典均是以dic结尾,换行符做为分隔,示例以下:
# sudo vim /opt/apps/elasticsearch-6.4.0/config/analysis-ik/custom/myDic.dic
修改ik配置文件,将自定义的词典添加到ik配置中
<entry key="ext_dict">custom/myDic.dic</entry>
重启es,注意必定要重启es
经过前面教程中,咱们发现短语"我爱祖国",会被分词为, "我","爱","祖国"三个词, 若是按照上面词典定义后, "我爱祖国"会被当成一个词语不被分词。
热更新远程词典的优点是,修改词典后无需重启es。每分钟加载一次
修改IK配置文件以下:
<!--用户能够在这里配置远程扩展字典 --> <entry key="remote_ext_dict">location</entry> <!--用户能够在这里配置远程扩展中止词字典--> <entry key="remote_ext_stopwords">location</entry>
其中 location
是指一个 url,好比 http://yoursite.com/getCustomDict
,该请求只需知足如下两点便可完成分词热更新。
该 http 请求须要返回两个头部(header),一个是 Last-Modified
,一个是 ETag
,这二者都是字符串类型,只要有一个发生变化,该插件就会去抓取新的分词进而更新词库。
该 http 请求返回的内容格式是一行一个分词,换行符用 \n
便可。
知足上面两点要求就能够实现热更新分词了,不须要重启 ES 实例。
能够将需自动更新的热词放在一个 UTF-8 编码的 .txt 文件里,放在 nginx 或其余简易 http server 下,当 .txt 文件修改时,http server 会在客户端请求该文件时自动返回相应的 Last-Modified 和 ETag。能够另外作一个工具来从业务系统提取相关词汇,并更新这个 .txt 文件。
本文将远程词典存入数据库,示例以下:
@RequestMapping(value = "/getCustomDict") public ResponseEntity<String> getCustomDict(WebRequest webRequest) { LOGGER.info("查询ik远程词典开始"); StringBuilder stringBuilder = new StringBuilder(); RemoteExtDictQuery query = new RemoteExtDictQuery(); query.setLastModifiedStart(new Date(1L)); query.setStatus(1); final long[] lastModified = {1L}; // 这是从数据库查询字典,若是词语不少的话,能够分页查询 List<RemoteExtDictEntity> remoteExtDictEntityList = remoteExtDictService.pageListRemoteExtDict(query); if (remoteExtDictEntityList == null) { remoteExtDictEntityList = new ArrayList<>(); } remoteExtDictEntityList.stream().forEach((remoteExtDictEntity) -> { if (remoteExtDictEntity != null) { if (StringUtils.isNotBlank(remoteExtDictEntity.getWord())) { stringBuilder.append(remoteExtDictEntity.getWord()).append("\n"); } if (remoteExtDictEntity.getLastModified() != null) { // 获取到最新一条的更新时间 long tempLastModified = remoteExtDictEntity.getLastModified().getTime(); if (Long.compare(lastModified[0], tempLastModified) < 0) { lastModified[0] = tempLastModified; } } } }); // 使用总条数做为etag String eTag = String.valueOf(remoteExtDictService.pageListRemoteExtDictCount(query)); if (webRequest.checkNotModified(eTag, lastModified[0])) { // 检查etag和 lastModified,若是没有变化则返回空,远程词典不更新,不然返回最新词典 return null; } else { return ResponseEntity.ok().contentType(MediaType.parseMediaType("text/plain; charset=UTF-8")) .body(stringBuilder.toString()); } }
截止目前,hanlp词库是最大,分词效果是最好。使用hanlp分词插件以前,建议先点击此处学习一下hanlp
hanlp的elasticsearch插件众多,这里选用了这个,这个插件支持的分词模式要多一些,截止如今此插件最新支持6.3.2,因为插件中包含很大的词典文件,建议此插件采用离线安装
# cd /opt/packages # sudo yum -y wget ## 因网络不通,可能须要很长时间 # sudo wget -c https://github.com/KennFalcon/elasticsearch-analysis-hanlp/releases/download/v6.3.2/elasticsearch-analysis-hanlp-6.3.2.zip ## 解压hanlp插件 # sudo unzip -n elasticsearch-analysis-hanlp-6.3.2.zip -d /opt/packages/elasticsearch-analysis-hanlp # sudo mkdir -p /opt/apps/elasticsearch-6.4.0/analysis-hanlp/config ## 将插件配置放到{ES_HOME}/config 目录下 # sudo mv elasticsearch-analysis-hanlp/config/ /opt/apps/elasticsearch-6.4.0/analysis-hanlp/config ## 将插件放到{ES_HOME}/plugins 目录下 # sudo mv elasticsearch-analysis-hanlp /opt/apps/elasticsearch-6.4.0/plugins/analysis-hanlp
查看插件安装列表
# sudo /opt/apps/elasticsearch-6.4.0/bin/elasticsearch-plugin list
注意: 这里有一个hanlp的警告,es版本是6.4.0, 可是插件容许的es版本是6.3.2
上面的警告须要修改一下插件配置, 本方法仅限于博主对应的版本哟,其余版本没去试验
# sudo vim /opt/apps/elasticsearch-6.4.0/plugins/analysis-hanlp/plugin-descriptor.properties
将 elasticsearch.version=6.3.2 修改成 elasticsearch.version=6.4.0,再次查看插件列表
ok,安装成功,安装完毕后必须重启es哟必须重启es哟必须重启es哟
# 词典目录,本文保持默认 root=plugins/analysis-hanlp/ # 核心词典目录,本文保持默认,多个词典使用分号分隔 CoreDictionaryPath=data/dictionary/CoreNatureDictionary.txt # Bigram词典目录,本文保持默认,多个词典使用分号分隔 BiGramDictionaryPath=data/dictionary/CoreNatureDictionary.ngram.txt # 核心停用词词典目录,本文保持默认,多个词典使用分号分隔 CoreStopWordDictionaryPath=data/dictionary/stopwords.txt # 核心同义词词典目录,本文保持默认,多个词典使用分号分隔 CoreSynonymDictionaryDictionaryPath=data/dictionary/synonym/CoreSynonym.txt # 人名词典目录,本文保持默认,多个词典使用分号分隔 PersonDictionaryPath=data/dictionary/person/nr.txt # 人名词典tr目录,本文保持默认,多个词典使用分号分隔 PersonDictionaryTrPath=data/dictionary/person/nr.tr.txt # tc词典目录,本文保持默认,多个词典使用分号分隔 tcDictionaryRoot=data/dictionary/tc # 自定义词典目录,根据最新词典设置,多个词典使用分号分隔 CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; SearchCustomDictionary.txt; 现代汉语补充词库.txt; 全国地名大全.txt ns; 人名词典.txt; 机构名词典.txt; 上海地名.txt ns;data/dictionary/person/nrf.txt nrf; # CRF segment model path #CRFSegmentModelPath=data/model/segment/CRFSegmentModel.txt # HMM segment model path #HMMSegmentModelPath=data/model/segment/HMMSegmentModel.bin # True of false show term nature #ShowTermNature=true # IO Adapter ##IOAdapter=com.hankcs.hanlp.corpus.io.FileIOAdapter
hanlp语料库地址为: https://github.com/hankcs/HanLP/releases, 本文截止目前最新版本为1.6.8
本文写完了中文分词器的做用和使用,下一篇将记录es客户端在项目中如何使用