热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

修改ik分词器源码实现直连数据库动态增量更新词汇

谈到es的中文分词器,肯定少不了ik分词器.现ik分词器有两种获取主词汇和停用词的方法:一是通过ik\config目录下的main.dic和stopword.dic获取,但是每次




 谈到es的中文分词器,肯定少不了ik分词器.现ik分词器有两种获取主词汇和停用词的方法:

一是通过ik\config目录下的main.dic和stopword.dic获取,但是每次修改后要重启才能生效
二是通过提供接口返回所有词汇的接口,接口路径配置在.但是该方式每次都需要将所有词汇返回,效率不高.

 本次目的就是通过jdbc直接连接数据库来实现增量更新词汇.我们要做的就是找到添加主词汇和停用词汇的方法,然后再通过jdbc获取数据库词汇来调用该方法来更新词汇

 下载ik源码,我下载的是7.17.6本版.因为es使用的是7.17.7,为防止启动报错,下载后我将版本改成了7.17.7.


词汇更新介绍

(1)找到Dictionary.initial方法
在这里插入图片描述
  可以看到,加载词汇的过程再Dictionary.initial 方法中,在该方法中,加载了各文件的词汇还有通过定时任务来获取接口词汇进行更新.
(2)接下来我们进入到singleton.loadMainDict -> loadExtDict -> loadDictFile方法中
在这里插入图片描述
  可以看到dict.fillSegment就是添加主词汇
(3)同理的,如下_stopWords.fillSegment就是对停用词的加载
在这里插入图片描述
  所以我们要做的就是拿到词汇,调用对应的fillSegment来加载词汇就可以了


准备工作

(1)表设计
 主词汇表:

CREATE TABLE `es_dic_main` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`word` varchar(100) NOT NULL COMMENT '词汇',
`moditime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`ifdel` char(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='主词汇'

 通用词表:

CREATE TABLE `es_dic_stop` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`word` varchar(100) NOT NULL COMMENT '停用词',
`moditime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`ifdel` char(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='停用词'

(2)在/config目录下创建jdbc配置文件jdbc.properties:

jdbc.url=jdbc:mysql://cckg.liulingjie.cn:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=账号
jdbc.password=密码
# 主词汇增量查询sql
main.word.sql=SELECT * FROM es_dic_main WHERE moditime >= ?
# 通用词增量查询sql
stop.word.sql=SELECT * FROM es_dic_stop WHERE moditime >= ?
# 执行间隔(秒)
interval=10

(3)pom.xml添加jdbc依赖:

<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.21version>
dependency>

&#xff08;4&#xff09;src/main/assemblies/plugin.xml下添加以下内容打包时包含mysql驱动jar包&#xff1a;

<dependencySet>
<outputDirectory/>
<useProjectArtifact>trueuseProjectArtifact>
<useTransitiveFiltering>trueuseTransitiveFiltering>
<includes>
<include>mysql:mysql-connector-javainclude>
includes>
dependencySet>

在这里插入图片描述


过程

 大致流程&#xff1a;
在这里插入图片描述
 主要涉及有两个类&#xff0c;一个是Dictionary&#xff0c;一个是自己创建的类JdbcMonitor。
  Dictionary&#xff1a;提供读取配置&#xff0c;加载词汇和启动词汇更新任务。
  JdbcMonitor功能&#xff1a;是一个实现了Runner接口的类&#xff0c;通过jdbc读取数据库词汇并调用Dictionary的方法加载词汇

&#xff08;1&#xff09;在Dictionary类中添加以下方法提供对词汇的api
在这里插入图片描述
 代码&#xff1a;

protected void fillSegmentMain(String word) {
_MainDict.fillSegment(word.trim().toCharArray());
}
protected void disableSegmentMain(String word) {
_MainDict.disableSegment(word.trim().toCharArray());
}
protected void fillSegmentStop(String word) {
_StopWords.fillSegment(word.trim().toCharArray());
}
protected void disableSegmentStop(String word) {
_StopWords.disableSegment(word.trim().toCharArray());
}

&#xff08;2&#xff09;在Dictionary构造方法中读取配置jdbc.properties
在这里插入图片描述
 代码&#xff1a;

public class JdbcConfig {
private String url;
private String username;
private String password;
private String mainWordSql;
private String stopWordSql;
private Integer interval;
// geter,setter省略
}

private Dictionary(Configuration cfg) {
//......省略
// 读取jdbc配置
setJdbcConfig();
}
private void setJdbcConfig() {
Path file &#61; PathUtils.get(getDictRoot(), Dictionary.PATH_JDBC_CONFIG);
Properties properties &#61; null;
try {
properties &#61; new Properties();
properties.load(new FileInputStream(file.toFile()));
} catch (Exception e) {
logger.error("load jdbc.properties failed");
logger.error(e.getMessage());
}
jdbcConfig &#61; new JdbcConfig(
properties.getProperty("jdbc.url"),
properties.getProperty("jdbc.username"),
properties.getProperty("jdbc.password"),
properties.getProperty("main.word.sql"),
properties.getProperty("stop.word.sql"),
Integer.valueOf(properties.getProperty("interval"))
);
}

&#xff08;3&#xff09;声明JdbcMinitor类定时连接数据库读取并更新词汇

package org.wltea.analyzer.dic;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.SpecialPermission;
import org.wltea.analyzer.cfg.JdbcConfig;
import org.wltea.analyzer.help.ESPluginLoggerFactory;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* &#64;author liulingjie
* &#64;date 2022/11/29 20:36
*/

public class JdbcMonitor implements Runnable {
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (Exception e) {
e.getStackTrace();
}
}
/**
* jdbc配置
*/

private JdbcConfig jdbcConfig;
/**
* 主词汇上次更新时间
*/

private Timestamp mainLastModitime &#61; Timestamp.valueOf("2022-01-01 00:00:00");
/**
* 停用词上次更新时间
*/

private Timestamp stopLastModitime &#61; Timestamp.valueOf("2022-01-01 00:00:00");
private static final Logger logger &#61; ESPluginLoggerFactory.getLogger(JdbcMonitor.class.getName());
public JdbcMonitor(JdbcConfig jdbcConfig) {
this.jdbcConfig &#61; jdbcConfig;
}
&#64;Override
public void run() {
SpecialPermission.check();
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
this.runUnprivileged();
return null;
});
}
/**
* 加载词汇和停用词
*/

public void runUnprivileged() {
//Dictionary.getSingleton().reLoadMainDict();
loadWords();
}
private void loadWords() {
List<String> mainWords &#61; new ArrayList<>();
List<String> delMainWords &#61; new ArrayList<>();
List<String> stopWords &#61; new ArrayList<>();
List<String> delStopWords &#61; new ArrayList<>();
setAllWordList(mainWords, delMainWords, stopWords, delStopWords);
mainWords.forEach(w -> Dictionary.getSingleton().fillSegmentMain(w));
delMainWords.forEach(w -> Dictionary.getSingleton().disableSegmentMain(w));
stopWords.forEach(w -> Dictionary.getSingleton().fillSegmentStop(w));
delStopWords.forEach(w -> Dictionary.getSingleton().disableSegmentStop(w));
logger.info("ik dic refresh from db. mainLastModitime: {} stopLastModitime: {}", mainLastModitime, stopLastModitime);
}
/**
* 获取主词汇和停用词
*
* &#64;param mainWords
* &#64;param delMainWords
* &#64;param stopWords
* &#64;param delStopWords
*/

private void setAllWordList(List<String> mainWords, List<String> delMainWords, List<String> stopWords, List<String> delStopWords) {
Connection connection &#61; null;
try {
connection &#61; DriverManager.getConnection(jdbcConfig.getUrl(), jdbcConfig.getUsername(), jdbcConfig.getPassword());
setWordList(connection, jdbcConfig.getMainWordSql(), mainLastModitime, mainWords, delMainWords);
setWordList(connection, jdbcConfig.getStopWordSql(), stopLastModitime, stopWords, delStopWords);
} catch (SQLException throwables) {
logger.error("jdbc load words failed: mainLastModitime-{} stopLostMOditime-{}", mainLastModitime, stopLastModitime);
logger.error(throwables.getStackTrace());
} finally {
if (connection !&#61; null) {
try {
connection.close();
} catch (SQLException throwables) {
logger.error("failed to close connection");
logger.error(throwables.getMessage());
}
}
}
}
/**
* 连接数据库获取词汇
*
* &#64;param connection
* &#64;param sql
* &#64;param lastModitime
* &#64;param words
* &#64;param delWords
*/

private void setWordList(Connection connection, String sql, Timestamp lastModitime, List<String> words, List<String> delWords) {
PreparedStatement prepareStatement &#61; null;
ResultSet result &#61; null;
try {
prepareStatement &#61; connection.prepareStatement(sql);
prepareStatement.setTimestamp(1, lastModitime);
result &#61; prepareStatement.executeQuery();
while (result.next()) {
String word &#61; result.getString("word");
Timestamp moditime &#61; result.getTimestamp("moditime");
String ifdel &#61; result.getString("ifdel");
if ("1".equals(ifdel)) {
delWords.add(word);
} else {
words.add(word);
}
// 取最大的时间
if (moditime.after(lastModitime)) {
lastModitime.setTime(moditime.getTime());
}
}
} catch (SQLException throwables) {
logger.error("jdbc load words failed: {}", lastModitime);
logger.error(throwables.getMessage());
} finally {
if (result !&#61; null) {
try {
result.close();
} catch (SQLException throwables) {
logger.error("failed to close prepareStatement");
logger.error(throwables.getMessage());
}
}
if (prepareStatement !&#61; null) {
try {
prepareStatement.close();
} catch (SQLException throwables) {
logger.error("failed to close prepareStatement");
logger.error(throwables.getMessage());
}
}
}
}
}

&#xff08;4&#xff09;最后在Dictionary.initial方法中启用该定时任务
在这里插入图片描述
 代码&#xff1a;

public static synchronized void initial(Configuration cfg) {
if (singleton&#61;&#61; null) {
synchronized (Dictionary.class) {
if (singleton &#61;&#61; null) {
singleton &#61; new Dictionary(cfg);
......
// 开启数据库增量更新
pool.scheduleAtFixedRate(new JdbcMonitor(singleton.jdbcConfig), 10, singleton.jdbcConfig.getInterval(), TimeUnit.SECONDS);
}
}
}
}

&#xff08;5&#xff09;最后mvn cliean package打包&#xff0c;在~\target\releases下会生成如下包在这里插入图片描述
&#xff08;6&#xff09;解压放入到 es安装路径/plugins/ik 重启es就行了







推荐阅读
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 解决Only fullscreen opaque activities can request orientation错误的方法
    本文介绍了在使用PictureSelectorLight第三方框架时遇到的Only fullscreen opaque activities can request orientation错误,并提供了一种有效的解决方案。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 本文详细介绍了 InfluxDB、collectd 和 Grafana 的安装与配置流程。首先,按照启动顺序依次安装并配置 InfluxDB、collectd 和 Grafana。InfluxDB 作为时序数据库,用于存储时间序列数据;collectd 负责数据的采集与传输;Grafana 则用于数据的可视化展示。文中提供了 collectd 的官方文档链接,便于用户参考和进一步了解其配置选项。通过本指南,读者可以轻松搭建一个高效的数据监控系统。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
  • MySQL 5.7 学习指南:SQLyog 中的主键、列属性和数据类型
    本文介绍了 MySQL 5.7 中主键(Primary Key)和自增(Auto-Increment)的概念,以及如何在 SQLyog 中设置这些属性。同时,还探讨了数据类型的分类和选择,以及列属性的设置方法。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 本文详细介绍了如何使用Python中的smtplib库来发送带有附件的邮件,并提供了完整的代码示例。作者:多测师_王sir,时间:2020年5月20日 17:24,微信:15367499889,公司:上海多测师信息有限公司。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 数据类型和操作数据表2.1MySQL类型之整型2.2MySQL数据类型之浮点型2.3日期时间型DATE1支持时间:1000年1月1日~9999年12月31日DATETIME ... [详细]
  • 如何在Linux服务器上配置MySQL和Tomcat的开机自动启动
    在Linux服务器上部署Web项目时,通常需要确保MySQL和Tomcat服务能够随系统启动而自动运行。本文将详细介绍如何在Linux环境中配置MySQL和Tomcat的开机自启动,以确保服务的稳定性和可靠性。通过合理的配置,可以有效避免因服务未启动而导致的项目故障。 ... [详细]
author-avatar
curlt_357
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有