基于web地址的方式实现ik分词热更新。
操作系统:win 11
es version:8.6.2
ik version:8.6.2
1、创建web服务,并提供ik查询词库接口
- 编写分词http url代码,返回自定义分词内容
- 分词词库数据来自业务需求,存储于业务DB,便于维护
- 多个分词按行输出,中文分词编码UTF-8
- http url接口返回内容编码UTF-8
- 设置返回header头信息 ETag 为自定义词库的hash值;
- 设置返回header头信息 Last-Modified 为自定义词库最新更新的时间
ETag和Last-Modified任意一个变化就会ik就会更新词库。
package com.david.ikremotedict.controller;
import com.david.ikremotedict.domain.IkExtDict;
import com.david.ikremotedict.domain.IkStopDict;
import com.david.ikremotedict.service.IkExtDictService;
import com.david.ikremotedict.service.IkStopDictService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
/**
* @authar David
* @Date 2025/4/1
* @description
*/
@Controller
@RequestMapping("/remoteDict")
public class RemoteDictController {
//自定义分词词库
@Autowired
private IkExtDictService extDictService;
@Autowired
private IkStopDictService stopDictService;
private final String DEFAULT_LAST_MODIFIED = "1743492903";
/**
* 扩展分词
*
* @return * @throws IOException
*/
@GetMapping(value = "extDict")
@ResponseBody
public ResponseEntity<StreamingResponseBody> ikExtDict() throws IOException {
System.out.println(LocalDateTime.now());
// 获取词库列表
List<IkExtDict> list = extDictService.list();
// 提前判断 list 是否为空,避免不必要的流式处理
if (list == null || list.isEmpty()) {
return ResponseEntity.noContent()
.header("ETag", "eb5b427b4d494525a6595a215df46dab")
.header("Last-Modified", DEFAULT_LAST_MODIFIED)
.build();
}
// 获取最新的创建时间戳
IkExtDict latestDict = list.get(list.size() - 1);
long timestamp = latestDict.getCreatedAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
// 创建流式响应体
StreamingResponseBody responseBody = outputStream -> {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
for (IkExtDict dict : list) {
writer.write(dict.getDict());
writer.newLine(); // 写入换行符
}
} catch (IOException exception) {
exception.printStackTrace(); // 抛出异常以便上层处理
}
};
// 返回响应实体
return ResponseEntity.ok()
.contentType(MediaType.valueOf("text/plain;charset=UTF-8"))
.header("ETag", "eb5b427b4d494525a6595a215df46dab")
// 这里应该返回操作dict的最后时间,先临时返回最后一个数据的创建时间
.header("Last-Modified", String.valueOf(timestamp))
.body(responseBody);
}
/**
* 扩展停止词
*
* @return * @throws IOException
*/
@GetMapping(value = "stopDict")
@ResponseBody
public ResponseEntity<StreamingResponseBody> ikStopDict() throws IOException {
List<IkStopDict> list = stopDictService.list();
// 提前判断 list 是否为空,避免不必要的流式处理
if (list == null || list.isEmpty()) {
return ResponseEntity.noContent()
.header("ETag", "eb5b427b4d494525a6595a215df46dab")
.header("Last-Modified", DEFAULT_LAST_MODIFIED)
.build();
}
// 获取最新的创建时间戳
IkStopDict latestDict = list.get(list.size() - 1);
long timestamp = latestDict.getCreatedAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
// 创建流式响应体
StreamingResponseBody responseBody = outputStream -> {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
for (IkStopDict dict : list) {
writer.write(dict.getDict());
writer.newLine(); // 写入换行符
}
} catch (IOException exception) {
exception.printStackTrace(); // 抛出异常以便上层处理
}
};
// 返回响应实体
return ResponseEntity.ok()
.contentType(MediaType.valueOf("text/plain;charset=UTF-8"))
.header("ETag", "5a6595a2eb5b427b4d4945215df46dab")
// 这里应该返回操作dict的最后时间,先临时返回最后一个数据的创建时间
.header("Last-Modified", String.valueOf(timestamp))
.body(responseBody);
}
}
源码地址 github:https://github.com/a66245753/ik-remote-dict.git
查询扩展词url:http://127.0.0.1:8080/remoteDict/extDict
查询停用词url:http://127.0.0.1:8080/remoteDict/stopDict
2、ik 插件下载配置
2.1、下载ik分词器地址:Index of:
2.2、进入analysis-ik
2.3、选择es对应版本 8.6.2
下载地址:https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-8.6.2.zip
2.4、在es插件目录下新建ik目录,{es-path}\plugins\ik
2.5、将2.3下载的压缩包解压到ik目录
2.6、在es的config下面创建analysis-ik目录,{es-path}\config\analysis-ik
2.7、将ik目录下config里面的文件拷贝到analysis-ik目录中
2.8、编辑 IKAnalyzer.cfg.xml,将web服务中的数据接口地址填进去
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict"></entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!-- 用户可以在这里配置远程扩展字典 -->
<entry key="remote_ext_dict">http://127.0.0.1:8080/remoteDict/extDict</entry>
<!-- 用户可以在这里配置远程扩展停止词字典 -->
<entry key="remote_ext_stopwords">http://127.0.0.1:8080/remoteDict/stopDict</entry>
</properties>
3、启动es和kibana
启动es的时候会看到加载plugin的日志和词库内容。
4、验证
4.1、没加“噜咪啦”分词前的解析
4.2、加“噜咪啦”分词后的解析
经过测试ik读取web地址的频率是每分钟一次,暂时没找到强制刷新的方法。
索引字段与属性都属于静态设置,若后期变更历史数据需要重建索引才可生效
对历史数据无效!!!!一定要重建索引!!!!
重建索引:es 3期 第10节 如何正确使用Reindex重建索引_es reindex 原索更新正常使用-CSDN博客
配置文件含义:
IKAnalyzer.cfg.xml:配置自定义词库文件
main.dic:内置的中文词库
quantifier.dic:单位相关的词
suffix.dic:后缀词
surname.dic:中国姓氏
stopword.dic:英文停用词
preposition.dic:介词词典