当前位置: 首页 > article >正文

ES IK分词字典热更新

前言

在使用IK分词器的时候,发现官方默认的分词不满足我们的需求,那么有没有方法可以自定义字典呢?

官方提供了三种方式

一、ik本地文件读取方式

k插件本来已为用户提供自定义词典扩展功能,只要修改配给文件即可:

1、添加自定义词典文件

在es/plugins/ik/config目录下创建mydict.dic文件;
mydict.dic文件中添加自定义热词;

2、修改IKAnalyzer.cfg.xml配置文件

在es/plugins/ik/config目录下修改IKAnalyzer.cfg.xml,修改内容如下:

/custom/mydict.dic

注意:mydict.dic文件格式为UTF-8

二、ik远程词库调用

1、ik官方文档说明

目前该插件支持热更新 IK 分词,通过上文在 IK 配置文件中提到的如下配置

location

location

其中 location 是指一个 url,比如 http://yoursite.com/getCustomDict,该请求只需满足以下两点即可完成分词热更新。
1.该 http 请求需要返回两个头部(header),一个是 Last-Modified,一个是 ETag,这两者都是字符串类型,只要有一个发生变化,该插件就会去抓取新的分词进而更新词库。
2.该 http 请求返回的内容格式是一行一个分词,换行符用 \n 即可。

2、http接口编写

@GetMapping("/keyWord/hot")
    public void getHotWordByOracle(HttpServletResponse response, Integer type) throws IOException {
        //查询分词字典
        if (type == null) {
            type = 0;
        }
        List<String> keyWords = keyWordDictService.getKeyWord(type);

        StringBuilder result = new StringBuilder();
        for (String value : keyWords){
            result.append(value+"\n");
        }

        result.delete(result.length()-1,result.length());

        try {
            String time = new Date().toString();
            response.setHeader("Last-Modified", time);
            response.setHeader("ETag",time);
            response.setContentType("text/plain; charset=utf-8");
            response.setBufferSize(500000);

            PrintWriter writer = response.getWriter();
            writer.write(result.toString());
        } catch (IOException e) {
            log.error("自定义词典更新报错" , e);
        }
    }

接口注意点:需要设置ContentType,根据业务需要BufferSize设置要大,并且不能使用flush

研究一下tomcat的相关特性。

在tomcat当中,是使用一个respone buffer的缓存来存储即将发回的数据,如果这个buffer没有使用完,默认的情况下,tomcat使用的就是常规的方式,就是一次性返回,这个时候在response header当中是有Content-Length。如果这个buffer写满了而你还有数据要歇的时候,这个时候就先要进行一次会写,这个时候tomcat的响应就变成了chuncked的模式了。还有一种情况,如果显示的进行flush操作,就是response.gerWriter wirter.flush也会导致变成chuncked响应。
因此,在springboot tomcat项目中,需要将buffer size设置的更大(具体看自己的业务需求),同时不能显式的去调用flush操作

三、MYSQL实现热更新词库

1、下载源码

下载地址:https://github.com/medcl/elasticsearch-analysis-ik/tree/v7.8.0
IK分词器版本要和ES版本一样

2、修改源码

添加 jdbc-reload.properties 配置文件

jdbc.url=jdbc:mysql://127.0.0.1:3307/test?serverTimezone=GMT
jdbc.user=root
jdbc.password=abc123456
jdbc.reload.sql=select word from hot_words
jdbc.reload.stopword.sql=select stopword as word from hot_stopwords
jdbc.reload.interval=1000

添加热更新线程类 HotDictReloadThread,就是一个死循环,不断调用Dictionary.getSingleton().reLoadMainDict(),去重新加载词典

public class HotDictReloadThread implements Runnable{

    private static final Logger LOGGER = ESPluginLoggerFactory.getLogger(HotDictReloadThread.class.getName());

    @Override
    public void run() {
        while (true){
            LOGGER.info("reload hot dict from mysql");
            Dictionary.getSingleton().reLoadMainDict();
        }
    }
}

修改 Dictionary类initial 初始化方法,创建一个我们自定义的线程,并且启动它

new Thread(new  HotDictReloadThread()).start();

在这里插入图片描述
增加从mysql加载扩展词典方法

private static Properties prop = new Properties();

	static {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e){
			logger.error("error",e);
		}
	}

	/**
	 * 从mysql加载扩展词典
	 */
	private void loadMySqlExtDict(){
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Path file = PathUtils.get(getDictRoot(),"jdbc-reload.properties");
			prop.load(new FileInputStream(file.toFile()));

			logger.info("jdbc-reload.properties");
			for (Object key : prop.keySet()) {
				logger.info(key + "=" + prop.getProperty(String.valueOf(key)));
			}

			logger.info("query hot dict from mysql," + prop.getProperty("jdbc.reload.sql"));

			conn = DriverManager.getConnection(
					prop.getProperty("jdbc.url"),
					prop.getProperty("jdbc.user"),
					prop.getProperty("jdbc.password")
			);
			stmt = conn.createStatement();
			rs = stmt.executeQuery(prop.getProperty("jdbc.reload.sql"));

			while (rs.next()){
				String word = rs.getString("word");
				logger.info("hot word from mysql:" + word);
				_MainDict.fillSegment(word.trim().toCharArray());
			}
			Thread.sleep(Integer.valueOf(String.valueOf(prop.get("jdbc.reload.interval"))));
		} catch (Exception e){
			logger.error("error",e);
		} finally {
			if (rs != null) {
				try {
					rs.close();
				} catch (SQLException e){
					logger.error("error",e);
				}
			}

			if (stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e){
					logger.error("error",e);
				}
			}

			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					logger.error("error",e);
				}
			}
		}
	}

并在加载主词典及扩展词典方法 loadMainDict 中调用
在这里插入图片描述
增加从mysql加载停用词方法 loadMySQLStopwordDict

/**
	 * 从mysql加载停用词
	 */
	private void loadMySQLStopwordDict() {
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;

		try {
			Path file = PathUtils.get(getDictRoot(), "jdbc-reload.properties");
			prop.load(new FileInputStream(file.toFile()));

			logger.info("[==========]jdbc-reload.properties");
			for(Object key : prop.keySet()) {
				logger.info("[==========]" + key + "=" + prop.getProperty(String.valueOf(key)));
			}

			logger.info("[==========]query hot stopword dict from mysql, " + prop.getProperty("jdbc.reload.stopword.sql") + "......");

			conn = DriverManager.getConnection(
					prop.getProperty("jdbc.url"),
					prop.getProperty("jdbc.user"),
					prop.getProperty("jdbc.password"));
			stmt = conn.createStatement();
			rs = stmt.executeQuery(prop.getProperty("jdbc.reload.stopword.sql"));

			while(rs.next()) {
				String theWord = rs.getString("word");
				logger.info("[==========]hot stopword from mysql: " + theWord);
				_StopWords.fillSegment(theWord.trim().toCharArray());
			}

			Thread.sleep(Integer.valueOf(String.valueOf(prop.get("jdbc.reload.interval"))));
		} catch (Exception e) {
			logger.error("erorr", e);
		} finally {
			if(rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					logger.error("error", e);
				}
			}
			if(stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					logger.error("error", e);
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					logger.error("error", e);
				}
			}
		}
	}

并在加载用户扩展的停止词词典方法 loadStopWordDict 中调用
在这里插入图片描述

3、打包

mvn package打包代码
把文件target\releases\elasticsearch-analysis-ik-7.8.0.zip放到es的plugins中

4、解压缩

将zip包解压,并把mysql驱动放到ik目录下

5、重启es

之后通过数据库添加分词或者停用词即可。


http://www.kler.cn/a/459053.html

相关文章:

  • Linux平台下实现的小程序-进度条
  • Python 字符串定义及前缀
  • 等待事件 ‘latch: row cache objects‘ 说明及解决方法
  • Singleton: WebRTC中ThreadManager中的单例模式
  • 【阻塞队列】- ArrayBlockingQueue 的原理-迭代器
  • Spring实现Logback日志模板设置动态参数
  • 从0开始的Opencv之旅(到尝试构建一个图像编辑器):0,opencv demo
  • Kotlin 协程基础知识总结五 —— 通道、多路复用、并发安全
  • 存储进阶笔记(二):Linux 存储栈:从 Device Mapper、LVM 到文件系统(2024)
  • 抽奖2(信奥)
  • springboot515基于SpringBoot的宠物爱心组织管理系统(论文+源码)_kaic
  • Python爬虫(selenium)从网站获取信息并存入数据库(mysql)
  • SCOPE:面向大语言模型长序列生成的双阶段KV缓存优化框架
  • 【2024年-9月-14日-开源社区openEuler实践记录】PM4OSSP-PROXY
  • 前端页面展示本电脑的摄像头,并使用js获取摄像头列表
  • css 类名
  • Tomcat:开源Web服务器的中流砥柱
  • 一款汽车连接器(HSD(4+2))信号完整性仿真
  • 资源规划管理系统(源码+文档+部署+讲解)
  • JVM实战—JVM内存设置与对象分配流转
  • 重生之我在异世界学编程之数据结构与算法:深入栈篇
  • 机器学习特征选择
  • NLP自然语言处理——使用飞桨实现基于LSTM的情感分析
  • Tonghttpserver6.0.1.3 使用整理(by lqw)
  • AMBA总线协议
  • 鸿蒙1.2:第一个应用