ES的默认中文分词效果太差了,稍微长一点的词句就完全匹配不到,于是选择使用安装IK中文分词器来实现索引的分词。

es写入数据 java es写入数据没有ik分词_json


一、安装

官网教程:

https://github.com/medcl/elasticsearch-analysis-ik,注意版本对应的问题

es写入数据 java es写入数据没有ik分词_json_02

1.下载

从此处下载预构建包:https ://github.com/medcl/elasticsearch-analysis-ik/releases

根据版本匹配,我使用的是ES7.10.2,因此要下载对应ik7.10.2(如果版本不匹配的话,ik分词器会无法使用)

es写入数据 java es写入数据没有ik分词_分词器_03


2.解压

在ES安装文件夹下的plugins文件夹下创建ik目录,将zip文件解压到ik目录下,删除zip

es写入数据 java es写入数据没有ik分词_json_04


3.重新启动ES后测试

(1)原生分词器效果

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

es写入数据 java es写入数据没有ik分词_es写入数据 java_05


(2)ik分词器效果

①ik_max_word

会把文本做最细粒度的解析,会穷尽各种可能的组合,适合词条查询;

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

es写入数据 java es写入数据没有ik分词_字段_06

②ik_smart

会做最粗粒度的拆分,适合短语查询。

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

es写入数据 java es写入数据没有ik分词_字段_07



二、项目使用

删除之前的索引,创建新的指定分词器的索引(对相应字段设定分词器),并将数据重新导入后测试检索效果

索引字段详解:

fields可以让同一文本有多种不同的索引方式,比如所示一个String类型的字段city,可以使用text类型做全文检索,使用keyword类型做聚合和排序。  

PUT index_name
{
  "mappings": {         # 设置 mappings
    "properties": {     # 属性,固定写法
      "city": {         # 字段名
        "type": "text", # city 字段的类型为 text
        "fields": {     # 多字段域,固定写法
          "raw": {      # 子字段名称
            "type":  "keyword"  # 子字段类型
            "ignore_above": 256  #在ElasticSearch中keyword,text类型字段ignore_above属性(动态映射默认是256) ,表示最大的字段值长度,超出这个长度的字段将不会被索引,查询不到,但是会存储。
          }
        }
      }
    }
  }
}

通过analyzer属性指定写入分词器采用细粒度模式ik_max_word;通过search_analyzer属性指定查询时采用智能模式ik_smart

es写入数据 java es写入数据没有ik分词_字段_08

1.创建json对象作为索引mapping

由于数据类型较多,使用json文件,将其装换为json对象

(1)pom.xml

<!--json文件转json对象-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.54</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>

(2)JsonUtil

package org.project.es.common.util;

import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import com.alibaba.fastjson.JSONObject;
/**
 * 将json文件装换为json对象
 * @author Administrator
 */
public class JsonUtil {
    public static JSONObject fileToJson(String fileName) {
        JSONObject json = null;
        try (
                InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
        ) {
            json = JSONObject.parseObject(IOUtils.toString(is, "utf-8"));
        } catch (Exception e) {
            System.out.println(fileName + "文件读取异常" + e);
        }
        return json;
    }
    public static void main(String[] args) {
        String fileName = "doc/policy.json";
        JSONObject json = JsonUtil.fileToJson(fileName);
        System.out.println(json);
    }
}

效果:

es写入数据 java es写入数据没有ik分词_分词器_09


2.创建索引

public static void createIndex(RestHighLevelClient client,String index) throws IOException {
        // 1.创建索引 - 请求对象
        CreateIndexRequest request = new CreateIndexRequest(index);
        // 2.设置setting,也就是索引的基本配置信息,将setting添加到请求对象中
        Settings setting = Settings.builder()
                //设置分片数,主分片数量一旦设置后就不能修改了
                .put("index.number_of_shards", 1)
                //索引的刷新时间间隔,索引更新多久才对搜索可见(即数据写入es到可以搜索到的时间间隔,设置越小越靠近实时,但是索引的速度会明显下降,),
                // 默认为1秒,如果我们对实时搜索没有太大的要求,反而更注重索引的速度,那么我们就应该设置的稍微大一些,这里设置30s
                .put("index.refresh_interval", "30s")
                //每个节点上允许最多分片数
                .put("index.routing.allocation.total_shards_per_node", 3)
                //将数据同步到磁盘的频率,为了保证性能,插入ES的数据并不会立刻落盘,而是首先存放在内存当中,
                // 等到条件成熟后触发flush操作,内存中的数据才会被写入到磁盘当中
                .put("index.translog.sync_interval", "30s")
                //每个主分片拥有的副本数,副本数量可以修改
                .put("index.number_of_replicas", 1)
                //一次最多获取多少条记录
                .put("index.max_result_window", "10000000")
                .build();
        request.settings(setting);

        // 3.使用工具类将json文件转换为json对象,添加到请求对象中
        JSONObject mapping = JsonUtil.fileToJson("doc/policy.json");
        request.mapping(mapping);

        // 4.发送请求,获取响应
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        boolean acknowledged = response.isAcknowledged();
        // 5.输出响应状态
        System.out.println("操作状态 = " + acknowledged);
    }

查看索引

es写入数据 java es写入数据没有ik分词_es写入数据 java_10


3.导入数据

索引创建好之后使用createDoc语句将mysql数据存入es(es支持批量创建doc,但是如果数量太大会报错,于是选择了依次创建),便于检索


4.测试检索

之前使用默认分词器的效果很差,检索结果不尽如人意,在安装使用了IK分词器后再次检索测试,发现效果不错,检索速度也很快

es写入数据 java es写入数据没有ik分词_分词器_11