IK分词器虽然自带词库

但是在实际开发应用中对于词库的灵活度的要求是远远不够的,IK分词器虽然配置文件中能添加扩展词库,但是需要重启ES

这章就当写一篇扩展了

其实IK本身是支持热更新词库的,但是需要我感觉不是很好

词库热更新方案:

1:IK 原生的热更新方案,部署一个WEB服务器,提供一个Http接口,通过Modified和tag两个Http响应头,来完成词库的热更新

2:通过修改IK源码支持Mysql定时更新数据

注意:推荐使用第二种方案,也是比较常用的方式,虽然第一种是官方提供的,但是官方也不建议使用

方案一:IK原生方案

1:外挂词库,就是在IK配置文件中添加扩展词库文件多个之间使用分号分割

优点:编辑指定词库文件,部署比较方便

缺点:每次编辑更新后都需要重启ES

2:远程词库,就是在IK配置文件中配置一个Http请求,可以是.dic文件,也可以是接口,同样多个之间使用分号分割

优点:指定静态文件,或者接口设置词库实现热更新词库,不用重启ES,是IK原生自带的

缺点:需要通过Modified和tag两个Http响应头,来提供词库的热更新,有时候会不生效

具体使用就不说了,在这里具体说第二种方案

方案二:通过定时读取Mysql完成词库的热更新

首先要下载IK分词器的源码

网址:https://github.com/medcl/elasticsearch-analysis-ik

下载的时候一定要选对版本,保持和ES的版本一致,否则会启动的时候报错,版本不一致

接着把源码导入IDEA中,并在POM.xml中添加Mysql的依赖,根据自己的Mysql版本需要添加

我的Mysql是5.6.1所以添加5的驱动包

mysql

mysql-connector-java

5.1.49

然后再config目录下创建一个新的.properties配置文件

在里面配置Mysql的一些配置,以及我们需要的配置

jdbc.url=jdbc:mysql://192.168.43.154:3306/es?characterEncoding=UTF-8&serverTimezone=GMT&nullCatalogMeansCurrent=true

jdbc.user=root

jdbc.password=root

# 更新词库

jdbc.reload.sql=select word from hot_words

# 更新停用词词库

jdbc.reload.stopword.sql=select stopword as word from hot_stopwords

# 重新拉取时间间隔

jdbc.reload.interval=5000

创建一个新的线程,用于调用Dictionary得reLoadMainDict()方法重新加载词库

packageorg.wltea.analyzer.dic;importorg.wltea.analyzer.help.ESPluginLoggerFactory;public class HotDicReloadThread implementsRunnable{private static final org.apache.logging.log4j.Logger logger = ESPluginLoggerFactory.getLogger(Dictionary.class.getName());

@Overridepublic voidrun() {while (true){

logger.info("-------重新加载mysql词典--------");

Dictionary.getSingleton().reLoadMainDict();

}

}

}

修改org.wltea.analyzer.dic文件夹下的Dictionary

在Dictionary类中加载mysql驱动类

private static Properties prop = newProperties();static{try{

Class.forName("com.mysql.jdbc.Driver");

}catch(ClassNotFoundException e) {

logger.error("error", e);

}

}

接着,创建重Mysql中加载词典的方法

/*** 从mysql中加载热更新词典*/

private voidloadMySqlExtDict(){

Connection connection= null;

Statement statement= null;

ResultSet resultSet= null;try{

Path file= PathUtils.get(getDictRoot(),"jdbc-reload.properties");

prop.load(newFileInputStream(file.toFile()));

logger.info("-------jdbc-reload.properties-------");for(Object key : prop.keySet()) {

logger.info("key:{}", prop.getProperty(String.valueOf(key)));

}

logger.info("------- 查询词典, sql:{}-------", prop.getProperty("jdbc.reload.sql"));//建立mysql连接

connection =DriverManager.getConnection(

prop.getProperty("jdbc.url"),

prop.getProperty("jdbc.user"),

prop.getProperty("jdbc.password")

);//执行查询

statement =connection.createStatement();

resultSet= statement.executeQuery(prop.getProperty("jdbc.reload.sql"));//循环输出查询啊结果,添加到Main.dict中去

while(resultSet.next()) {

String theWord= resultSet.getString("word");

logger.info("------热更新词典:{}------", theWord);//加到mainDict里面

_MainDict.fillSegment(theWord.trim().toCharArray());

}

}catch(Exception e) {

logger.error("error:{}", e);

}finally{try{if (resultSet != null) {

resultSet.close();

}if (statement != null) {

statement.close();

}if (connection != null) {

connection.close();

}

}catch(SQLException e){

logger.error("error", e);

}

}

}

接着,创建加载停用词词典方法

/*** 从mysql中加载停用词*/

private voidloadMySqlStopwordDict(){

Connection conn= null;

Statement stmt= null;

ResultSet rs= null;try{

Path file= PathUtils.get(getDictRoot(), "jdbc-reload.properties");

prop.load(newFileInputStream(file.toFile()));

logger.info("-------jdbc-reload.properties-------");for(Object key : prop.keySet()) {

logger.info("-------key:{}", prop.getProperty(String.valueOf(key)));

}

logger.info("-------查询停用词, sql:{}",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("------- 加载停用词 : {}", theWord);

_StopWords.fillSegment(theWord.trim().toCharArray());

}

Thread.sleep(Integer.valueOf(String.valueOf(prop.get("jdbc.reload.interval"))));

}catch(Exception e) {

logger.error("error", e);

}finally{try{if(rs != null) {

rs.close();

}if(stmt != null) {

stmt.close();

}if(conn != null) {

conn.close();

}

}catch(SQLException e){

logger.error("error:{}", e);

}

}

}

接下来,分别在loadMainDict()方法和loadStopWordDict()方法结尾处调用

/*** 加载主词典及扩展词典*/

private voidloadMainDict() {//建立一个主词典实例

_MainDict = new DictSegment((char) 0);//读取主词典文件

Path file =PathUtils.get(getDictRoot(), Dictionary.PATH_DIC_MAIN);

loadDictFile(_MainDict, file,false, "Main Dict");//加载扩展词典

this.loadExtDict();//加载远程自定义词库

this.loadRemoteExtDict();//加载Mysql外挂词库

this.loadMySqlExtDict();

}

/*** 加载用户扩展的停止词词典*/

private voidloadStopWordDict() {//建立主词典实例

_StopWords = new DictSegment((char) 0);//读取主词典文件

Path file =PathUtils.get(getDictRoot(), Dictionary.PATH_DIC_STOP);

loadDictFile(_StopWords, file,false, "Main Stopwords");//加载扩展停止词典

List extStopWordDictFiles =getExtStopWordDictionarys();if (extStopWordDictFiles != null) {for(String extStopWordDictName : extStopWordDictFiles) {

logger.info("[Dict Loading] " +extStopWordDictName);//读取扩展词典文件

file =PathUtils.get(extStopWordDictName);

loadDictFile(_StopWords, file,false, "Extra Stopwords");

}

}//加载远程停用词典

List remoteExtStopWordDictFiles =getRemoteExtStopWordDictionarys();for(String location : remoteExtStopWordDictFiles) {

logger.info("[Dict Loading] " +location);

List lists =getRemoteWords(location);//如果找不到扩展的字典,则忽略

if (lists == null) {

logger.error("[Dict Loading] " + location + " load failed");continue;

}for(String theWord : lists) {if (theWord != null && !"".equals(theWord.trim())) {//加载远程词典数据到主内存中

logger.info(theWord);

_StopWords.fillSegment(theWord.trim().toLowerCase().toCharArray());

}

}

}//加载Mysql停用词词库

this.loadMySqlStopwordDict();

}

最后在initial()方法中启动更新线程

/*** 词典初始化 由于IK Analyzer的词典采用Dictionary类的静态方法进行词典初始化

* 只有当Dictionary类被实际调用时,才会开始载入词典, 这将延长首次分词操作的时间 该方法提供了一个在应用加载阶段就初始化字典的手段

*

*@returnDictionary*/

public static synchronized voidinitial(Configuration cfg) {if (singleton == null) {synchronized (Dictionary.class) {if (singleton == null) {

singleton= newDictionary(cfg);

singleton.loadMainDict();

singleton.loadSurnameDict();

singleton.loadQuantifierDict();

singleton.loadSuffixDict();

singleton.loadPrepDict();

singleton.loadStopWordDict();//执行更新mysql词库的线程

new Thread(newHotDicReloadThread()).start();if(cfg.isEnableRemoteDict()){//建立监控线程

for(String location : singleton.getRemoteExtDictionarys()) {//10 秒是初始延迟可以修改的 60是间隔时间 单位秒

pool.scheduleAtFixedRate(new Monitor(location), 10, 60, TimeUnit.SECONDS);

}for(String location : singleton.getRemoteExtStopWordDictionarys()) {

pool.scheduleAtFixedRate(new Monitor(location), 10, 60, TimeUnit.SECONDS);

}

}

}

}

}

}

然后,修改src/main/assemblies/plugin.xml文件中,加入Mysql

/

true

true

mysql:mysql-connector-java

源码到此修改完成,在自己的数据库中创建两张新的表

建表SQL

CREATE TABLE hot_words (

id bigint(20) NOT NULL AUTO_INCREMENT,

word varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '词语',

PRIMARY KEY (id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE hot_stopwords (

id bigint(20) NOT NULL AUTO_INCREMENT,

stopword varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '停用词',

PRIMARY KEY (id)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

接下来对源码进行打包:

打包之前检查自己的POM.xml中的elasticsearch.version的版本,记得和自己的ES的版本对应,否则到时候会报错

检查完毕后,点击IDEA右侧的package进行项目打包,如果版本不对,修改版本并点击IDEA右侧的刷新同步,进行版本的更换,然后打包

打包完成后在左侧项目中会出现target目录,会看到一个zip,我的是因为解压了,所以有文件夹

点击右键在文件夹中展示,然后使用解压工具解压

解压完成后,双击进入

先把原来ES下的plugins下的IK文件夹中的东西删除,可以先备份,然后把自己打包解压后里面的东西全部拷贝到ES下的plugins下的IK文件夹中

接下来进入bin目录下启动就可以了

当然按照惯例,我的启动时不会那么简单的,很高兴,我的报错了,所有的坑都踩了一遍,之前的版本不对就踩了两次

第一次是源码下载的版本不对

第二次的ES依赖版本不对

好了说报错:报错只贴主要内容

第三次报错:

Caused by: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "setContextClassLoader")

这个是JRE的类的创建设值权限不对

在jre/lib/security文件夹中有一个java.policy文件,在其grant{}中加入授权即可

permission java.lang.RuntimePermission "createClassLoader";

permission java.lang.RuntimePermission"getClassLoader";

permission java.lang.RuntimePermission"accessDeclaredMembers";

permission java.lang.RuntimePermission"setContextClassLoader";

第四次报错:

Caused by: java.security.AccessControlException: access denied ("java.net.SocketPermission" "192.168.43.154:3306" "connect,resolve")

这个是通信链接等权限不对

也是,在jre/lib/security文件夹中有一个java.policy文件,在其grant{}中加入授权即可

permission java.net.SocketPermission "192.168.43.154:3306","accept";

permission java.net.SocketPermission"192.168.43.154:3306","listen";

permission java.net.SocketPermission"192.168.43.154:3306","resolve";

permission java.net.SocketPermission"192.168.43.154:3306","connect";

到此之后启动无异常

最后就是测试了,启动我的head插件和kibana,这两个没有或者不会的可以看我之前写的,也可以百度

执行分词

但是我想要  天青色

在Mysql中添加记录

insert into hot_words(word) value("天青色");

重新执行

也比如我想要这就是一个词  天青色等烟雨

在Mysql中添加记录

insert into hot_words(word) value("天青色等烟雨");

再次执行

到此实现了ES定时从mysql中读取热词,停用词这个一般用的比较少,有兴趣自己测测,在使用的时候,通过业务系统往数据库热词表和停用词表添加记录就可以了

作者:彼岸舞

时间:2020\09\13

内容关于:ElasticSearch

本文来源于网络,只做技术分享,一概不负任何责任

mysql读数据入库es_ES 实现实时从Mysql数据库中读取热词,停用词相关推荐

  1. mysql读数据入库es_es数据库查询

    DB 与 Elasticsearch 混合之应用系统场景分析探讨 作者介绍 李猛,Elastic Stack深度用户,通过Elastic工程师认证,2012年接触Elasticsearch,对Elas ...

  2. mysql升级后将旧数据迁移到新 数据库中

    mysql升级后将旧数据迁移到新 数据库中 1.将原来数据库进行导出成.sql文件 例:StudentSql.sql 2.进入新数据库的bin目录下 复制bin目录 3.cmd进入bin目录下(需管理 ...

  3. 从mysql数据库中读取二进制文件_数据库技术之在MYSQL数据库中如何存取二进制文件...

    [数据库技术之在MYSQL数据库中如何存取二进制文件]在MySQL数据库中,不可以存放文字信息,还可以存放图片.声音.文件等二进制信息.那么在MYSQL数据库中中如何存取二进制文件?下面,武汉宏鹏鲁广 ...

  4. mysql 导出表数据到另一张表_yz-Mysql数据库中一个表中的数据导出来到另外一个数据库的表格...

    -----------方式一-------------------------------------------------------------------------------------- ...

  5. mysql 查询不为0的数据_查询数据库中所有记录总数不为0的数据表名称

    [如何查询postgreSQL 里面某个数据库中所有用户定义的数据表的名字@forandever 2011-11-131.通过命令行查询\d 数据库  -- 得到所有表的名字\d 表名  -- 得到表 ...

  6. MySQL利用磁盘缓存写入_MySQL写入缓冲区在数据库中的作用( Change Buffer )

    原标题:MySQL写入缓冲区在数据库中的作用( Change Buffer ) 介绍另外一种重要的数据变更日志,也就是InnoDB change buffer.Change buffer的主要目的是将 ...

  7. mysql 读取oracle数据_Python中Pandas通过read_sql方法从Mysql或Oracle数据库中读取数据帧(DataFrame)...

    1.pandas.read_sql(sql,con,index_col = None,coerce_float = True,params = None,parse_dates = None,colu ...

  8. 使用python实现微博评论分词与关键词提取(从MySQL数据库中读取数据)

    一.实验环境 (1)Windows操作系统: (2)PyCharm 2019.1. (3)数据库用户名为 root,密码为 123456. (4)  学校机房电脑,带有mysql 二.获取数据库信息 ...

  9. Mysql表数据如何导入到微信云开发数据库中

    文章目录 1.方法1 2.方法2 3.小结 思路:打开微信云开发控制平台,可以利用.json文件导入数据,所以重点是如何将mysql表数据导出为json文件 1.方法1 使用Navicat Premi ...

最新文章

  1. Go Pro 半小时上手指南
  2. 非线性规划-三种常见参数估计算法及联系
  3. java.io.IOException: Stream closed
  4. HP5200打印机从控制面板手动配置TCP/IP 参数
  5. 微软鼠标测试软件,微软发了一款“精准”鼠标 我们告诉你精准在哪儿?
  6. C++字符读入函数(getchgetchar)
  7. 如何使用EA画ER图?
  8. 面试题单例模式的五种写法(枚举妙用)
  9. 三维全景融合拼接技术
  10. 脱机使用打印机是什么意思?
  11. 页面布局中遇到菱形图片时的处理办法
  12. 计算机音乐nevada,Nevada
  13. Cocos2d-JS 中游戏背景音乐与音效
  14. Python · 助力冬奥:turtle绘制奥运五环
  15. PC网站微信扫码支付,Native支付,“当前商户号暂不支持关联该类型的appid“,“签名错误,请检查后再试““springBoot 微信支付“
  16. (VS)c++调用matlab函数
  17. Vue如何将baes64格式的图片转成普通格式
  18. 四轴飞行器——电调校准
  19. 视频剪辑工作者的福音,视频格式转换工具4Videosoft Video Converter Ultimate的介绍使用,可以转换所有的视频格式
  20. Ackerman函数(递归与非递归算法实现)

热门文章

  1. 终极解密!输入网址按回车到底发生了什么?
  2. 一文读懂 CI/CD 工具
  3. 中国最优秀的程序员都有哪些?王兴、张小龙、张一鸣是哪类?| 蒋涛说
  4. 万字梳理,带你拿下 Java 面试题!
  5. 定了!百度运维工程师非法挖矿获利 10 万、被判 3 年,如何避免面向监狱编程?...
  6. 谁说国产编译器没救了?这个 C/C++ 和 JavaScript 编译器来了 | 程序人生 2020
  7. 《2019年数据及存储发展研究报告》十大洞察
  8. 告别写笔记,AI 实时转写黑科技来了!
  9. 直接拿来用!GitHub 标星 8W,学完它就可以去 Google 面试了!
  10. 雷军这么努力,为什么还是干不过 OV?