博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何在Elasticsearch中安装中文分词器(IK+pinyin)
阅读量:5144 次
发布时间:2019-06-13

本文共 9369 字,大约阅读时间需要 31 分钟。

如果直接使用Elasticsearch的朋友在处理中文内容的搜索时,肯定会遇到很尴尬的问题——中文词语被分成了一个一个的汉字,当用Kibana作图的时候,按照term来分组,结果一个汉字被分成了一组。

这是因为使用了Elasticsearch中默认的标准分词器,这个分词器在处理中文的时候会把中文单词切分成一个一个的汉字,因此引入中文的分词器就能解决这个问题。

本篇文章按照下面的内容进行描述:

  • 分词器的作用
  • 安装IK
  • 简单的测试
  • 模拟测试
  • 安装elasticsearch-analysis-pinyin
  • 简单的测试
  • 模拟测试

分词器的作用

分词顾名思义,就是把一句话分成一个一个的词。这个概念在搜索中很重要,比如 This is a banana. 如果按照普通的空格来分词,分成this,is,a,banana,的出来的a其实对我们并没有什么用处。因此需要注意下面的问题:

  • 1 区分停顿词(a,or,and这种都属于停顿词)
  • 2 大小写转换(Bananabanana)
  • 3 时态的转换....

具体的算法可以参考,对照的词语可以参考这里

相比中文,就复杂的度了。因为中文不能单纯的依靠空格,标点这种进行分词。就比如中华人民共和国国民,不能简单的分成一个词,也不能粗暴的分成中华人民共和国国民人民中华这些也都算一个词!

因此常见的分词算法就是拿一个标准的词典,关键词都在这个词典里面。然后按照几种规则去查找有没有关键词,比如:

  • 正向最大匹配(从左到右)
  • 逆向最大匹配(从右到左)
  • 最少切分
  • 双向匹配(从左扫描一次,从右扫描一次)

提供了两种方式,ik_smart就是最少切分,ik_max_word则为细粒度的切分(可能是双向,没看过源码)

了解了分词器的背景后,就可以看一下如何在Elasticsearch重安装分词器了。

安装IK

在,比如我的最新版本2.4.0就没有对应的ik版本,不用担心,只需要修改pom.xml就可以了:

2.4.0
1.7
${project.basedir}/src/main/assemblies/plugin.xml
analysis-ik
org.elasticsearch.plugin.analysis.ik.AnalysisIkPlugin
true
false
true
4E899B30
true

下载后,执行mvn package,进行打包:

├─config├─src└─target    ├─archive-tmp    ├─classes    ├─generated-sources    ├─maven-archiver    ├─maven-status    ├─releases    │  └─elasticsearch-analysis-ik-1.9.5.zip    └─surefire

编译完成后,可以在target/releases目录下找到对应的zip包。

解压zip包,复制到elasticsearch-root-path/plugins/ik下即可。

[root@hadoop-master ik]# lltotal 1428-rw-r--r-- 1 root root 263965 Sep 26 15:03 commons-codec-1.9.jar-rw-r--r-- 1 root root  61829 Sep 26 15:03 commons-logging-1.2.jardrwxr-xr-x 3 root root   4096 Sep 26 16:11 config-rw-r--r-- 1 root root  56023 Sep 26 15:03 elasticsearch-analysis-ik-1.9.5.jar-rw-r--r-- 1 root root 736658 Sep 26 15:03 httpclient-4.5.2.jar-rw-r--r-- 1 root root 326724 Sep 26 15:03 httpcore-4.4.4.jar-rw-r--r-- 1 root root   2666 Sep 26 15:03 plugin-descriptor.properties[root@hadoop-master ik]# pwd/usr/elk/elasticsearch-2.4.0/plugins/ik

拷贝后,重启elasticsearch就可以使用分词器了。

最简单的测试

这里使用_analyze api对中文段落进行分词,测试一下:

GET _analyze{  "analyzer":"ik_max_word",  "text":"中华人民共和国国歌"}

可以看到ik尽可能多的切分的单词:

{  "tokens": [    {      "token": "中华人民共和国",      "start_offset": 0,      "end_offset": 7,      "type": "CN_WORD",      "position": 0    },    {      "token": "中华人民",      "start_offset": 0,      "end_offset": 4,      "type": "CN_WORD",      "position": 1    },    {      "token": "中华",      "start_offset": 0,      "end_offset": 2,      "type": "CN_WORD",      "position": 2    },    {      "token": "华人",      "start_offset": 1,      "end_offset": 3,      "type": "CN_WORD",      "position": 3    },    {      "token": "人民共和国",      "start_offset": 2,      "end_offset": 7,      "type": "CN_WORD",      "position": 4    },    {      "token": "人民",      "start_offset": 2,      "end_offset": 4,      "type": "CN_WORD",      "position": 5    },    {      "token": "共和国",      "start_offset": 4,      "end_offset": 7,      "type": "CN_WORD",      "position": 6    },    {      "token": "共和",      "start_offset": 4,      "end_offset": 6,      "type": "CN_WORD",      "position": 7    },    {      "token": "国",      "start_offset": 6,      "end_offset": 7,      "type": "CN_CHAR",      "position": 8    },    {      "token": "国歌",      "start_offset": 7,      "end_offset": 9,      "type": "CN_WORD",      "position": 9    }  ]}

如果使用ik_smart,则会尽可能少的返回词语:

{  "tokens": [    {      "token": "中华人民共和国",      "start_offset": 0,      "end_offset": 7,      "type": "CN_WORD",      "position": 0    },    {      "token": "国歌",      "start_offset": 7,      "end_offset": 9,      "type": "CN_WORD",      "position": 1    }  ]}

模拟测试

我这里直接在elastic Sense中进行测试的(强烈推荐这个插件,非常好用,不过输入中文的时候,有点BUG)

第一步,创建一个空的索引

PUT test {  }

如果你用的是curl,可以执行curl -XPUT localhost:9200/test

第二步,设置映射类型

POST test/test/_mapping{    "test": {        "_all": {            "analyzer": "ik_max_word",            "search_analyzer": "ik_max_word",            "term_vector": "no",            "store": "false"        },        "properties": {            "content": {                "type": "string",                "store": "no",                "term_vector": "with_positions_offsets",                "analyzer": "ik_max_word",                "search_analyzer": "ik_max_word",                "include_in_all": "true",                "boost": 8            }        }    }}

上面的命令,是定义test索引下test类型的映射。其中定义了_all字段的分析方法,以及content属性的分析方法。

这里介绍下,其实_all字段是为了在不知道搜索哪个字段时,使用的。es会把所有的字段(除非你手动设置成false),都放在_all中,然后通过分词器去解析。当你使用query_string的时候,默认就在这个_all字段上去做查询,而不需要挨个字段遍历,节省了时间。

properties中定义了特定字段的分析方式。在上面的例子中,仅仅设置了content的分析方法。

  • type,字段的类型为string,只有string类型才涉及到分词,像是数字之类的是不需要分词的。
  • store,定义字段的存储方式,no代表不单独存储,查询的时候会从_source中解析。当你频繁的针对某个字段查询时,可以考虑设置成true。
  • term_vector,定义了词的存储方式,with_position_offsets,意思是存储词语的偏移位置,在结果高亮的时候有用。
  • analyzer,定义了索引时的分词方法
  • search_analyzer,定义了搜索时的分词方法
  • include_in_all,定义了是否包含在_all字段中
  • boost,是跟计算分值相关的。

设置完成后,添加一个文档

POST test/test/1{  "test":"美国留给伊拉克的是个烂摊子吗"}POST test/test/2{  "content":"公安部:各地校车将享最高路权吗"}POST test/test/3{  "content":"中韩渔警冲突调查:韩警平均每天扣1艘中国渔船"}POST test/test/4{  "content":"中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首"}

最后,执行查询进行测试

GET test/_search{  "query" : { "term" : { "content" : "中国" }},  "highlight" : {        "pre_tags" : ["
", "
"], "post_tags" : ["
", ""], "fields" : { "content" : {} } }}

得到返回结果:

{  "took": 4,  "timed_out": false,  "_shards": {    "total": 5,    "successful": 5,    "failed": 0  },  "hits": {    "total": 2,    "max_score": 1.5,    "hits": [      {        "_index": "test",        "_type": "test",        "_id": "4",        "_score": 1.5,        "_source": {          "content": "中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首"        },        "highlight": {          "content": [            "
中国
驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首" ] } }, { "_index": "test", "_type": "test", "_id": "3", "_score": 0.53699243, "_source": { "content": "中韩渔警冲突调查:韩警平均每天扣1艘中国渔船" }, "highlight": { "content": [ "中韩渔警冲突调查:韩警平均每天扣1艘
中国
渔船" ] } } ] }}

安装elasticsearch-analysis-pinyin分词器

pinyin分词器可以让用户输入拼音,就能查找到相关的关键词。比如在某个商城搜索中,输入shuihu,就能匹配到水壶。这样的体验还是非常好的。

pinyin分词器的安装与IK是一样的,这里就省略掉了。下载的地址.

这个分词器在1.8版本中,提供了两种分词规则:

  • pinyin,就是普通的把汉字转换成拼音;
  • pinyin_first_letter,提取汉字的拼音首字母

简单的测试

首先创建索引,并创建分词器:

PUT medcl{  "index" : {        "analysis" : {            "analyzer" : {                "pinyin_analyzer" : {                    "tokenizer" : "my_pinyin",                    "filter" : "word_delimiter"                    }            },            "tokenizer" : {                "my_pinyin" : {                    "type" : "pinyin",                    "first_letter" : "none",                    "padding_char" : " "                }            }        }    }}

然后使用analyze api,进行测试

GET medcl/_analyze{  "text":"刘德华",  "analyzer":"pinyin_analyzer"}

可以得到结果:

{  "tokens": [    {      "token": "liu",      "start_offset": 0,      "end_offset": 3,      "type": "word",      "position": 0    },    {      "token": "de",      "start_offset": 0,      "end_offset": 3,      "type": "word",      "position": 1    },    {      "token": "hua",      "start_offset": 0,      "end_offset": 3,      "type": "word",      "position": 2    }  ]}

如果分词器设置为pinyin_first_letter,则分析的结果为:

{  "tokens": [    {      "token": "ldh",      "start_offset": 0,      "end_offset": 3,      "type": "word",      "position": 0    }  ]}

模拟测试

如果索引已经存在,需要先关闭索引

POST medcl/_close{  }

然后设置分词器配置

PUT medcl/_settings {  "index" : {        "analysis" : {            "analyzer" : {                "pinyin_analyzer" : {                    "tokenizer" : "my_pinyin",                    "filter" : ["word_delimiter","nGram"]                }            },            "tokenizer" : {                "my_pinyin" : {                    "type" : "pinyin",                    "first_letter" : "prefix",                    "padding_char" : " "                }            }        }    }}

打开索引

POST medcl/_open{  }

定义映射类型

POST medcl/folks/_mapping{  "folks": {        "properties": {            "name": {                "type": "multi_field",                "fields": {                    "name": {                        "type": "string",                        "store": "no",                        "term_vector": "with_positions_offsets",                        "analyzer": "pinyin_analyzer",                        "boost": 10                    },                    "primitive": {                        "type": "string",                        "store": "yes",                        "analyzer": "keyword"                    }                }            }        }    }}

提交样例数据

POST medcl/folks/1{  "name":"刘德华"}

执行查询

GET medcl/folks/_search{ "query": {"match": {   "name": "l d hua" }}}

这里搜liu de hua,ldh,l de hua都能匹配到,还是很强大滴。

得到结果

{  "took": 7,  "timed_out": false,  "_shards": {    "total": 5,    "successful": 5,    "failed": 0  },  "hits": {    "total": 1,    "max_score": 7.408082,    "hits": [      {        "_index": "medcl",        "_type": "folks",        "_id": "1",        "_score": 7.408082,        "_source": {          "name": "刘德华"        }      }    ]  }}

参考

  • 1
  • 2
  • 3
  • 4

转载于:https://www.cnblogs.com/xing901022/p/5910139.html

你可能感兴趣的文章
php随机生成文章,PHP生成随机字符串的技巧
查看>>
Linux内核存储接口,内存管理 – Linux内核API kcalloc
查看>>
c语言除法的数据类型,c语言数据类型运算的注意
查看>>
android模拟器游戏大全,安卓模拟器游戏大全_小鸡模拟器
查看>>
android%3cspan,Microsoft 365 Apps update channel name changes: iOS, Mac, and Android
查看>>
js生成GUID
查看>>
JdbcTemplate
查看>>
利用PreparedStatement预防SQL注入
查看>>
saltsack自动化配置day01:之SaltStack快速入门(一)
查看>>
剑指offer-----回溯和其他
查看>>
Java核心技术卷1-第三章-Java的基本程序设计结构
查看>>
从C++到Java的几点区别
查看>>
【牛客Wannafly挑战赛12】小H和圣诞树
查看>>
[AH2017/HNOI2017]单旋
查看>>
[SNOI2017]一个简单的询问
查看>>
【CF900D】Unusual Sequences
查看>>
[ZJOI2019]线段树
查看>>
[WC2018]通道
查看>>
LGP5495 Dirichlet 前缀和
查看>>
[PKUSC2018]神仙的游戏
查看>>