大数据求索(10): 解决ElasticSearch中文搜索无结果-----IK中文分词器的安装与使用
问题所在
在中文情况下,ES默认分词器会将汉字切分为一个一个的汉字,所以当搜索词语的时候,会发现无法找到结果。
解决办法
有很多其他的分词器插件可以替代,这里使用最常用的IK分词器。
IK分词器安装
注意:
ik分词器必须和ES版本严格对应,否则可能会产生意料之外的错误。
Github地址:https://github.com/medcl/elasticsearch-analysis-ik
可以直接下载releases下面的源码,使用maven进行编译。
把下载的elasticsearch-analysis-ik.zip解压
进入elasticsearch-analysis-ik-master/ 下,打包。
mvn clean package
打包后的文件在elasticsearch-analysis-ik-master/target/relearses
目录下,名称类似如下,是一个zip文件。
elasticsearch-analysis-ik-5.6.2.zip
在/home/hadoop/cluster/elasticsearch/plugins创建ik文件夹,并将打包后的zip文件解压到此文件夹下,此时ik目录下一般有如下几个文件
commons-codec-1.9.jar config httpclient-4.5.2.jar plugin-descriptor.properties
commons-logging-1.2.jar elasticsearch-analysis-ik-5.6.2.jar httpcore-4.4.4.jar
此时,重启ES即可。
IK分词器的使用
使用IK分词器,由于认识较浅,只找到一种方法,就是改变ES中index的mapping。此外,由于mapping一旦创建就无法修改,所以,只能重新创建一个新的index,里面没数据 ,并设置mapping。java代码举例如下:
/**
* 重新生成mapping,使用ik分词器
* @throws Exception
*/
@Test
public void setMapping() throws Exception {
TransportClient client = getClient();
// 设置mapping,使用ik分词器
// 没有数据之前才能成功,如果已经有mapping,则无法重新创建
XContentBuilder builder = XContentFactory.jsonBuilder()
.startObject()
.startObject("article")
.startObject("properties")
.startObject("title").field("type", "text").field("store", "yes").field("analyzer", "ik_smart").field("search_analyzer","ik_max_word").endObject()
.endObject()
.endObject()
.endObject();
PutMappingRequest mapping = Requests.putMappingRequest("blog2").type("article").source(builder);
client.admin().indices().putMapping(mapping).actionGet();
client.close();
}
重新生产mapping以后,可以通过head插件查看,发现新的index的mapping如下:
{
"state": "open",
"settings": {
"index": {
"creation_date": "1545450199648",
"number_of_shards": "5",
"number_of_replicas": "1",
"uuid": "1GGDZoVqQV6YsRv0Sbu3gg",
"version": {
"created": "5060299"
},
"provided_name": "blog2"
}
},
"mappings": {
"article": {
"properties": {
"id": {
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"title": {
"search_analyzer": "ik_max_word",
"analyzer": "ik_smart",
"store": true,
"type": "text"
},
注意到title的search_analyzer和analyzer已经修改为ik分词器了,下面进行测试,首先插入两条数据,如下所示
{
"_index": "blog2",
"_type": "article",
"_id": "2",
"_version": 1,
"_score": 1,
"_source": {
"id": "2",
"title": "基于深度学习的搜索",
"content": "test"
},
{
"_index": "blog2",
"_type": "article",
"_id": "1",
"_version": 1,
"_score": 1,
"_source": {
"id": "1",
"title": "基于Lucene的搜索服务器",
"content": "提供分布式全文搜索"
}
}
}
进行模糊查询,代码如下
/**
* 模糊查询
*/
@Test
public void fuzzy() throws Exception {
TransportClient client = getClient();
SearchResponse searchResponse = client.prepareSearch("blog2")
.setTypes("article")
// 重新构造分词器以后才可以搜索成功,否则默认将汉字切分为单个字,所以无法检索到结果
.setQuery(QueryBuilders.fuzzyQuery("title", "基于"))
.get();
// 获取命中次数,查询结果有多少对
SearchHits hits = searchResponse.getHits();
System.out.println("查询结果有:" + hits.getTotalHits() + "条");
Iterator<SearchHit> iterator = hits.iterator();
while (iterator.hasNext()) {
SearchHit searchHit = iterator.next();
System.out.println(searchHit.getSourceAsString());
}
client.close();
}
此时结果如下
查询结果有:2条
{"id":"2","title":"基于深度学习的搜索","content":"test"}
{"id":"1","title":"基于Lucene的搜索服务器","content":"提供分布式全文搜索"}
可以进行词语级别的查询,IK分词器使用成功。
IK不同版本之间可能有小的差异,具体可以参考官方github配置。