IK分词器升级,MySQL热更新助一臂之力
ik分词器采用MySQL热更新
官方所给的IK分词器只支持远程文本文件热更新,不支持采用MySQL热更新,没关系,这难不倒伟大的博主,给哈哈哈。今天就来和大家讲一下如何采用MySQL做热更新IK分词器的词库。
一、建立数据库表
CREATE TABLE `es_extra_main`
(`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`word` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '词',`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已删除',`update_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP (6) COMMENT '更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `es_extra_stopword`
(`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',`word` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '词',`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否已删除',`update_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP (6) COMMENT '更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
二、修改IK分词器插件源码
2. 1修改pom文件
<!--mysql驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version>
</dependency>
2.2 新增DatabaseMonitor类
这里新增一个关于MySQL的类,源码中有关于远程文本文件的热更新源码,我们这边仿照源码来写一就可以啦。
package org.wltea.analyzer.dic;import org.apache.logging.log4j.Logger;
import org.elasticsearch.SpecialPermission;
import org.wltea.analyzer.help.ESPluginLoggerFactory;import java.security.AccessController;
import java.security.PrivilegedAction;
import java.sql.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;public class DatabaseMonitor implements Runnable {private static final Logger logger = ESPluginLoggerFactory.getLogger(DatabaseMonitor.class.getName());public static final String PATH_JDBC_PROPERTIES = "jdbc.properties";private static final String JDBC_URL = "jdbc.url";private static final String JDBC_USERNAME = "jdbc.username";private static final String JDBC_PASSWORD = "jdbc.password";private static final String JDBC_DRIVER = "jdbc.driver";private static final String SQL_UPDATE_MAIN_DIC = "jdbc.update.main.dic.sql";private static final String SQL_UPDATE_STOPWORD = "jdbc.update.stopword.sql";/*** 更新间隔*/public final static String JDBC_UPDATE_INTERVAL = "jdbc.update.interval";private static final Timestamp DEFAULT_LAST_UPDATE = Timestamp.valueOf(LocalDateTime.of(LocalDate.of(2020, 1, 1), LocalTime.MIN));private static Timestamp lastUpdateTimeOfMainDic = null;private static Timestamp lastUpdateTimeOfStopword = null;public String getUrl() {return Dictionary.getSingleton().getProperty(JDBC_URL);}public String getUsername() {return Dictionary.getSingleton().getProperty(JDBC_USERNAME);}public String getPassword() {return Dictionary.getSingleton().getProperty(JDBC_PASSWORD);}public String getDriver() {return Dictionary.getSingleton().getProperty(JDBC_DRIVER);}public String getUpdateMainDicSql() {return Dictionary.getSingleton().getProperty(SQL_UPDATE_MAIN_DIC);}public String getUpdateStopwordSql() {return Dictionary.getSingleton().getProperty(SQL_UPDATE_STOPWORD);}/*** 加载MySQL驱动*/public DatabaseMonitor() {SpecialPermission.check();AccessController.doPrivileged((PrivilegedAction<Void>) () -> {try {Class.forName(getDriver());} catch (ClassNotFoundException e) {logger.error("mysql jdbc driver not found", e);}return null;});}@Overridepublic void run() {SpecialPermission.check();AccessController.doPrivileged((PrivilegedAction<Void>) () -> {Connection conn = getConnection();// 更新主词典updateMainDic(conn);// 更新停用词updateStopword(conn);closeConnection(conn);return null;});}public Connection getConnection() {Connection connection = null;try {connection = DriverManager.getConnection(getUrl(), getUsername(), getPassword());} catch (SQLException e) {logger.error("failed to get connection", e);}return connection;}public void closeConnection(Connection conn) {if (conn != null) {try {conn.close();} catch (SQLException e) {logger.error("failed to close Connection", e);}}}public void closeRsAndPs(ResultSet rs, PreparedStatement ps) {if (rs != null) {try {rs.close();} catch (SQLException e) {logger.error("failed to close ResultSet", e);}}if (ps != null) {try {ps.close();} catch (SQLException e) {logger.error("failed to close PreparedStatement", e);}}}/*** 主词典*/public synchronized void updateMainDic(Connection conn) {logger.info("start update main dic");int numberOfAddWords = 0;int numberOfDisableWords = 0;PreparedStatement ps = null;ResultSet rs = null;try {String sql = getUpdateMainDicSql();Timestamp param = lastUpdateTimeOfMainDic == null ? DEFAULT_LAST_UPDATE : lastUpdateTimeOfMainDic;logger.info("param: " + param);ps = conn.prepareStatement(sql);ps.setTimestamp(1, param);rs = ps.executeQuery();while (rs.next()) {String word = rs.getString("word");word = word.trim();if (word.isEmpty()) {continue;}lastUpdateTimeOfMainDic = rs.getTimestamp("update_time");if (rs.getBoolean("is_deleted")) {logger.info("[main dic] disable word: {}", word);// 删除Dictionary.disableWord(word);numberOfDisableWords++;} else {logger.info("[main dic] add word: {}", word);// 添加Dictionary.addWord(word);numberOfAddWords++;}}logger.info("end update main dic -> addWord: {}, disableWord: {}", numberOfAddWords, numberOfDisableWords);} catch (SQLException e) {logger.error("failed to update main_dic", e);// 关闭 ResultSet、PreparedStatementcloseRsAndPs(rs, ps);}}/*** 停用词*/public synchronized void updateStopword(Connection conn) {logger.info("start update stopword");int numberOfAddWords = 0;int numberOfDisableWords = 0;PreparedStatement ps = null;ResultSet rs = null;try {String sql = getUpdateStopwordSql();Timestamp param = lastUpdateTimeOfStopword == null ? DEFAULT_LAST_UPDATE : lastUpdateTimeOfStopword;logger.info("param: " + param);ps = conn.prepareStatement(sql);ps.setTimestamp(1, param);rs = ps.executeQuery();while (rs.next()) {String word = rs.getString("word");word = word.trim();if (word.isEmpty()) {continue;}lastUpdateTimeOfStopword = rs.getTimestamp("update_time");if (rs.getBoolean("is_deleted")) {logger.info("[stopword] disable word: {}", word);// 删除Dictionary.disableStopword(word);numberOfDisableWords++;} else {logger.info("[stopword] add word: {}", word);// 添加Dictionary.addStopword(word);numberOfAddWords++;}}logger.info("end update stopword -> addWord: {}, disableWord: {}", numberOfAddWords, numberOfDisableWords);} catch (SQLException e) {logger.error("failed to update main_dic", e);} finally {// 关闭 ResultSet、PreparedStatementcloseRsAndPs(rs, ps);}}
}
2.3 修改代码
初始化方法中新增加载JDBC的方法和将getProperty改为public

并且在Dictionary类后面新增下面的方法
/*** 加载新词条*/public static void addWord(String word) {singleton._MainDict.fillSegment(word.trim().toLowerCase().toCharArray());}/*** 移除(屏蔽)词条*/public static void disableWord(String word) {singleton._MainDict.disableSegment(word.trim().toLowerCase().toCharArray());}/*** 加载新停用词*/public static void addStopword(String word) {singleton._StopWords.fillSegment(word.trim().toLowerCase().toCharArray());}/*** 移除(屏蔽)停用词*/public static void disableStopword(String word) {singleton._StopWords.disableSegment(word.trim().toLowerCase().toCharArray());}/*** 加载 jdbc.properties*/public void loadJdbcProperties() {Path file = PathUtils.get(getDictRoot(), DatabaseMonitor.PATH_JDBC_PROPERTIES);try {props.load(new FileInputStream(file.toFile()));logger.info("====================================properties====================================");for (Map.Entry<Object, Object> entry : props.entrySet()) {logger.info("{}: {}", entry.getKey(), entry.getValue());}logger.info("====================================properties====================================");} catch (IOException e) {logger.error("failed to read file: " + DatabaseMonitor.PATH_JDBC_PROPERTIES, e);}}
三、修改插件的权限

grant {// needed because of the hot reload functionalitypermission java.net.SocketPermission "*", "connect,resolve";permission java.lang.RuntimePermission "setContextClassLoader";
};
四、打包
4.1 加入依赖
将MySQL的jar包依赖加入进来,否则打包会缺少jar包保持错。

<include>mysql:mysql-connector-java</include>
4.2 package
打包成zip文件,然后加压成文件夹

五、安装
将解压的文件夹放到ES的plugins目录下,然后配置一下config目录下的数据库配置信息,最后再重启一下ES即可完成安装。

六、测试验证
在数据库表中中新增下面自己的想要的关键词,然后去Kibana中做测试验证,可以发现已经可以啦。
关键词

停止词

POST _analyze
{"text": ["俺是熊二呗"], "analyzer": "ik_max_word"
}
运行结果


相关文章:
IK分词器升级,MySQL热更新助一臂之力
ik分词器采用MySQL热更新 官方所给的IK分词器只支持远程文本文件热更新,不支持采用MySQL热更新,没关系,这难不倒伟大的博主,给哈哈哈。今天就来和大家讲一下如何采用MySQL做热更新IK分词器的词库。 一、建立数据库表 CREATE…...
泛微 E-Office文件上传漏洞复现
声明 本文仅用于技术交流,请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。 文章作者拥有对此文章的修改和解释权。如欲转载或传播此文章,…...
bug的生命周期
bug的生命周期 bugbug的生命周期bug等级 bug 当且仅当规格说明书是存在的并且正确的,程序和规格说明书之间的不匹配才是错误当产品规格说明书没有提到时,以用户需求为准,当程序最终没有实现用户的合理预期的功能要求时,就是软件错…...
mysql分库分表相关
3小时快速上手sharding-jdbc 百亿级数据 分库分表 后面怎么分页查询? Java实战:教你如何进行数据库分库分表...
云原生k8s---资源限制、探针
目录 一:资源限制 1、资源限制原因 2、Pod 和 容器 的资源请求和限制 3、CPU 资源单位 4、内存 资源单位 5、事例 (1)事例一 (2)事例二 二:重启策略 1、重启策略模式 2、事例 三:探针…...
html2canvas生成图片地址Base64格式转成blob在转成file(二进制)可正常发送(保姆教程,复制粘贴可用)
开始: 最终结果: 1. html2canvas方法生成的图片地址已Base64编码形式放在img标签src中可直接展示生成的图片(注意页面标签获取位置,还有个setTimeout页面渲染需要时间) setTimeout(function () {var result {};v…...
将Linux上的cpolar内网穿透配置为开机自启动——“cpolar内网穿透”
将Linux上的cpolar内网穿透配置为开机自启动 文章目录 将Linux上的cpolar内网穿透配置为开机自启动前言一、进入命令行模式二、输入token码三、输入内网穿透命令 前言 我们将cpolar安装到了Ubuntu系统上,并通过web-UI界面对cpolar的功能有了初步了解。当然cpolar除…...
微信小程序data-item设置获取不到数据的问题
微信小程序data-item设置获取不到数据的问题 简单说明: 在微信小程序中,通过列表渲染使用wx:for根据数组中的每一项重复渲染组件。同时使用bindtap给每一项绑定点击事件clickItem,再通过data-item绑定数据。 **问题:**通过data-i…...
创建百度百科需要什么条件?
随着互联网的发展,人们越来越依赖于搜索引擎获取信息。百度作为中国最大的搜索引擎之一,旗下的百科词条已成为人们获取知识的重要来源。创建百度百科需要什么条件呢?接下来伯乐网络传媒就来给大家讲一讲。 首先,你需要有一个百度…...
【springboot启动报错】java: 错误: 无效的源发行版:17
报错截图 解决方案 第一步:编辑配置,改为想用的jdk版本 第二步:文件--->项目结构,改为对应的SDK 第三步:文件--->设置--->构建、执行、部署--->编译器--->Java编译器,修改目标字节码版本 第…...
无涯教程-Perl - setservent函数
描述 在第一次调用getservent之前,应先调用此函数。 STAYOPEN参数是可选的,在大多数系统上未使用。当getservent()检索服务数据库中下一行的信息时,然后setervent设置(或重置)枚举到主机条目集的开头。 语法 以下是此函数的简单语法- setservent STAYOPEN返回值 此函数不返…...
Java创建多线程的最全方法
Java创建多线程的最全方法 一、继承Thread,重写run方法二、实现Runnable接口,重写run方法三、使用匿名内部类创建 Thread 子类对象四、使用匿名内部类,实现Runnable接口五、实现Callable接口六、使用线程池创建线程 一、继承Thread࿰…...
02 qt基本控件及信号和槽
一 QString类 功能:显示一个字符串内容 主要接口函数 构造函数: QString(const char *str)QString(const QString &other)赋值运算符重载: QString &operator=(const QString &other)功能函数: 1&...
大数据校招学员实习面试分享
本文实习面试总结来自一位非科班(机械专业)出身的在校生。 作为一个大数据领域的校招实习生,我在这里想分享一下我的经验和教训,希望对大家有所帮助。 1 简历投递准备 在准备简历时,首先需要准确地把握自己的技能和…...
用于弥散加权MRI的关节各向异性维纳滤光片研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
谷粒商城第十一天-品牌管理中关联分类
目录 一、总述 二、前端部分 1. 调整查询调用 2. 关联分类 三、后端部分 四、总结 一、总述 之前是在商品的分类管理中直接使用的若依的逆向代码 有下面的几个问题: 1. 表格上面的参数填写之后,都是按照完全匹配进行搜索,没有模糊匹配…...
Selenium自动化测试实战之自动化测试基础
自动化测试概念 是把以人为驱动的测试转化为机器执行的一种过程,它是一种以程序测试程序的过程。 自动化只是测试方式,跟测试阶段无关。 可以把任何测试工作写一个程序自动化实现都可以称为自动化测试。 selenium自动化测试:2023最新的Sele…...
vue3+vite中使用postcss-px-to-viewport适配问题
适配方案postcss-px-to-viewport使用过程中出现以下问题: postcss-px-to-viewport 不适配最新版本的postcss8 ⚠️报错: postcss-px-to-viewport: postcss.plugin was deprecated. Migration guide: https://evilmartians.com/chronicles/postcss-8-plugin-migrati…...
web测试与app测试的区别
web测试与app测试的区别 首先从系统架构来看的话: web项目,一般都是b/s架构,基于浏览器的,而app则是c/s的,必须要有客户端。那么在系统测试测试的时候就会产生区别了。 web测试只要更新了服务器端,客户端…...
深入理解高并发编程 - 分析创建线程池究竟有哪些方式
1、使用Executors工厂方法: 使用Executors工厂方法创建线程池是一种简单快捷的方式,适用于一些常见的线程池需求。以下是几个示例,演示如何使用Executors工厂方法创建不同类型的线程池: 固定大小线程池 (newFixedThreadPool)&am…...
LeaguePrank:英雄联盟段位修改与个性化展示完全指南
LeaguePrank:英雄联盟段位修改与个性化展示完全指南 【免费下载链接】LeaguePrank 项目地址: https://gitcode.com/gh_mirrors/le/LeaguePrank 想要在英雄联盟客户端中展示与众不同的段位和个性化信息吗?LeaguePrank 正是你需要的工具。这款开源…...
Open-AutoGLM场景实战:电商购物、出行旅游、内容浏览一键完成
Open-AutoGLM场景实战:电商购物、出行旅游、内容浏览一键完成 1. 引言:手机AI助手的革命性突破 想象一下这样的场景:早上醒来,你对手机说"帮我订一杯星巴克拿铁和一份三明治",手机自动完成打开外卖应用、选…...
像素语言·跨维传送门参数详解:Hunyuan-MT-7B引擎温度/长度/对齐策略调优指南
像素语言跨维传送门参数详解:Hunyuan-MT-7B引擎温度/长度/对齐策略调优指南 1. 工具概览与核心价值 像素语言跨维传送门(Pixel Language Portal)是基于Tencent Hunyuan-MT-7B引擎构建的创新翻译工具,它将传统翻译体验重构为16-bit像素冒险风格。不同于…...
MGeo地址结构化实战:对接RPA机器人自动填写政务表格中的标准地址字段
MGeo地址结构化实战:对接RPA机器人自动填写政务表格中的标准地址字段 1. 引言:当RPA机器人遇上“不标准”的地址 想象一下这个场景:你是一家政务服务中心的技术负责人,每天有成百上千份表格需要处理。其中,地址信息填…...
张毕贺的音乐故事《越说越明》
张毕贺的音乐故事,始于一把吉他,成于不懈创作,最终汇成一条连接梦想与大众的河流。他的音乐历程,既是个人才华的绽放,也是对音乐教育与本土文化推广的坚定投入。 音乐之路:从翻唱走红到原创深耕 张毕贺的…...
AI数字遗产:OpenClaw+Gemma-3-12b-it自动化整理与加密个人数据
AI数字遗产:OpenClawGemma-3-12b-it自动化整理与加密个人数据 1. 当技术遇上数字永生:一个程序员的私人实验 三年前祖母离世时,我在整理她的遗物时发现了一个装满老照片的饼干盒。那些褪色的相纸背后用铅笔写着模糊的日期和人名,…...
Windows热键冲突终结者:Hotkey Detective让键盘操作回归掌控
Windows热键冲突终结者:Hotkey Detective让键盘操作回归掌控 【免费下载链接】hotkey-detective A small program for investigating stolen key combinations under Windows 7 and later. 项目地址: https://gitcode.com/gh_mirrors/ho/hotkey-detective 当…...
05_Cursor之自定义规则与配置
关键字:.cursorrules, 自定义规则, AI模型配置, 文档集成, 终端集成, Cursor配置 05_Cursor之自定义规则与配置 Cursor知识体系 Cursor知识体系(续) | -- 配置定制层 | -- .cursorrules规则文件 | | -- 项目编码规范 | | -- 风格指…...
终极指南:web3.py Gas价格策略如何优化以太坊交易成本
终极指南:web3.py Gas价格策略如何优化以太坊交易成本 【免费下载链接】web3.py A python interface for interacting with the Ethereum blockchain and ecosystem. 项目地址: https://gitcode.com/gh_mirrors/we/web3.py web3.py 作为以太坊区块链的 Pytho…...
Local SDXL-Turbo应用案例:独立开发者构建个人AI绘画SaaS产品的技术栈选型
Local SDXL-Turbo应用案例:独立开发者构建个人AI绘画SaaS产品的技术栈选型 1. 引言:从想法到产品,一个开发者的选择 如果你是一名独立开发者,或者是一个小团队的负责人,想做一个自己的AI绘画工具,你可能会…...
