elasticsearch实战 修改IK源码实现词组动态更新
下载IK源码
https://github.com/medcl/elasticsearch-analysis-ik/tree/v5.2.0
选择你对应ik的版本(ps:版本最好一致)
http://localhost:9200/?pretty查看es版本 我的是6.5.1
修改源码
1.创建一个ext包同时增加3个类文件
DBHelper
package org.wltea.analyzer.ext; import org.apache.logging.log4j.Logger;
import org.elasticsearch.common.logging.Loggers; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map; public class DBHelper {
Logger logger= Loggers.getLogger(DBHelper.class); public static String url = null;
public static String dbUser = null;
public static String dbPwd = null;
public static String dbTable = null;
private Connection conn;
public static Map<String, Date> lastImportTimeMap = new HashMap<String, Date>(); static{
try {
Class.forName("com.mysql.jdbc.Driver");// 加载Mysql数据驱动
} catch (Exception e) {
e.printStackTrace();
}
}
private Connection getConn() throws Exception {
try {
conn = DriverManager.getConnection(url, dbUser, dbPwd);// 创建数据连接
} catch (Exception e) {
logger.warn("异常了");
e.printStackTrace();
}
return conn;
} /**
* 从数据库获得分词信息
* @param key 字段名
* @param type 分词类型 0扩展分词 1停分词
* @param delete 是否有效 0有效 1无效
* @param flag 是否每次加载最新的
* @param synonyStr
* @return
* @throws Exception
*/
public String getKey(String key, Integer type,boolean delete,boolean flag,String synonyStr) throws Exception { conn = getConn();
StringBuilder data = new StringBuilder();
PreparedStatement ps = null;
ResultSet rs = null;
try {
StringBuilder sql = new StringBuilder("select * from " + dbTable + " where 1=1");
//lastImportTime 最新更新时间
Date lastImportTime = DBHelper.lastImportTimeMap.get(key);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (lastImportTime != null && flag) {
sql.append(" and update_time > '" + sdf.format(lastImportTime) + "'");
}
sql.append(" and " + key + " !=''");
if(type!=null){
sql.append("and word_type="+type);
}
if(delete){
sql.append(" and delete_type="+1);
}else{
sql.append(" and delete_type="+0);
}
lastImportTime = new Date();
lastImportTimeMap.put(key,lastImportTime);
//如果打印出来的时间 和本地时间不一样,则要注意JVM时区是否和服务器系统时区一致
logger.warn("sql==={}",sql.toString());
System.out.print(conn);
ps = conn.prepareStatement(sql.toString());
rs = ps.executeQuery();
while (rs.next()) {
String value = rs.getString(key);
if (StringUtils.isNotBlank(value)) {
if (StringUtils.isNotBlank(synonyStr)) {
data.append(value + synonyStr);
} else {
data.append(value + ",");
}
} }
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (ps != null) {
ps.close(); }
if (rs != null) {
rs.close();
} conn.close(); } catch (Exception e) {
e.printStackTrace();
}
}
return data.toString();
}
//测试
// public static void main(String[] args) throws Exception {
// DBHelper dbHelper=new DBHelper();
// String extWords=dbHelper.getKey("ext_word",true);
// List<String> extList = Arrays.asList(extWords.split(","));
// System.out.println(extList);
// // System.out.println(getKey("stopword"));
// // System.out.println(getKey("synonym"));
// LocalDate now=LocalDate.now();
//
// } }
DBRunnable
package org.wltea.analyzer.ext; import org.apache.logging.log4j.Logger;
import org.elasticsearch.common.logging.Loggers;
import org.wltea.analyzer.dic.Dictionary; import java.util.Arrays;
import java.util.List; public class DBRunnable implements Runnable {
Logger logger = Loggers.getLogger(DBRunnable.class);
private String wordField; public DBRunnable(String wordField) {
super();
this.wordField = wordField;
} @Override
public void run() {
logger.warn("开始加载词库========");
//获取词库
Dictionary dic = Dictionary.getSingleton();
DBHelper dbHelper = new DBHelper();
try {
String extWords = dbHelper.getKey(wordField, 0,false,true,",");
String stopWords = dbHelper.getKey(wordField, 1,false,true,",");
String extDelWords = dbHelper.getKey(wordField, 0,true,true,",");
String extStopWords = dbHelper.getKey(wordField, 1,true,true,",");
if(StringUtils.isNotBlank(extWords)){
List<String> extList = Arrays.asList(extWords.split(","));
//把扩展词加载到主词库中
dic.addWords(extList);
logger.warn("加载扩展词成功========");
logger.warn("extWords为==={}",extWords);
}
if(StringUtils.isNotBlank(stopWords)){
List<String> stopList = Arrays.asList(stopWords.split(","));
//把扩展词加载到主词库中
dic.addStopWords(stopList);
logger.warn("加载停用词成功========");
logger.warn("stopWords为==={}",stopWords);
}
//移除词库
if(StringUtils.isNotBlank(extDelWords)){
List<String> stopList = Arrays.asList(extDelWords.split(","));
//把扩展词加载到主词库中
dic.disableWords(stopList);
logger.warn("移除扩展词成功========");
logger.warn("extDelWords==={}",extDelWords);
}
if(StringUtils.isNotBlank(extStopWords)){
List<String> stopList = Arrays.asList(extStopWords.split(","));
//把扩展词加载到主词库中
dic.disableStopWords(stopList);
logger.warn("移除停用词成功========");
logger.warn("extStopWords==={}",extStopWords);
} } catch (Exception e) { logger.warn("加载扩展词失败========{}",e);
} } }
StringUtils
package org.wltea.analyzer.ext; public class StringUtils {
/**
* 判断字符串是否为空 为空返回true 否则返回false
* @param str
* @return
*/
public static boolean isBlank(String str) {
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if ((Character.isWhitespace(str.charAt(i)) == false)) {
return false;
}
}
return true;
}
/**
* 判断字符串是否不为空 为空返回false 否则返回true
* @param str
* @return
*/
public static boolean isNotBlank(String str) {
return !StringUtils.isBlank(str);
}
}
2.Dictionary增加几个方法
/**
* 批量加载新停用词条
*
* @param words
* Collection<String>词条列表
*/
public void addStopWords(Collection<String> words) {
if (words != null) {
for (String word : words) {
if (word != null) {
// 批量加载词条到主内存词典中
_StopWords.fillSegment(word.trim().toCharArray());
}
}
} }
/**
* 批量移除停用词条
*
* @param words
* Collection<String>词条列表
*/
public void disableStopWords(Collection<String> words) {
if (words != null) {
for (String word : words) {
if (word != null) {
// 批量加载词条到主内存词典中
_StopWords.disableSegment(word.trim().toCharArray());
}
}
} }
/**
* 读取jdbc配置初始化 定时更新数据库词组定时任务
*
* @throws IOException
*/
public void initReloadMysqlWordJob() throws IOException { logger.warn("============IKAnalyzer==============");
Path file = PathUtils.get(getDictRoot(), "jdbc.properties");
Properties prop = new Properties();
prop.load(new FileInputStream(file.toFile()));
logger.info("===========load jdbc.properties========");
for(Object key : prop.keySet()) {
logger.info("==========>>" + key + "=" + prop.getProperty(String.valueOf(key)));
}
boolean autoReloadDic=Boolean.valueOf(prop.getProperty("autoReloadDic"));
if(autoReloadDic){
String dbUser = prop.getProperty("dbUser");
String dbPwd = prop.getProperty("dbPwd");
//获取每隔多久从数据库更新信息 默认60S
Integer flushTime = Integer.valueOf(prop.getProperty("flushTime"));
String dbTable = prop.getProperty("dbTable","t_es_ik_dic");
DBHelper.dbTable=dbTable;
DBHelper.dbUser=dbUser;
DBHelper.dbPwd=dbPwd;
DBHelper.url=prop.getProperty("dbUrl");
String wordFieldName = prop.getProperty("wordFieldName");
ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new DBRunnable(wordFieldName), 0, flushTime, TimeUnit.SECONDS);
}
}
4.在init方法启用job
public static synchronized Dictionary initial(Configuration cfg) {
if (singleton == null) {
synchronized (Dictionary.class) {
if (singleton == null) { singleton = new Dictionary(cfg);
singleton.loadMainDict();
singleton.loadSurnameDict();
singleton.loadQuantifierDict();
singleton.loadSuffixDict();
singleton.loadPrepDict();
singleton.loadStopWordDict();
try {
singleton.initReloadMysqlWordJob();
} catch (IOException e) {
logger.error("动态加载mysql词组失败....");
e.printStackTrace();
}
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);
}
} return singleton;
}
}
}
return singleton;
}
将ik安装导入es
1.打包
2.将zip文件移动到es的plugins文件夹
解压并重命名为ik
3.ik目录的config创建一个jdbc.properties文件
dbUrl=jdbc:mysql://ip/port #数据库连接
dbUser=user #数据库用户名
dbPwd=password #数据库密码
dbTable=md_es_ik_dic #词库表
wordFieldName=word #词组字段
flushTime=5 #刷新时间 (秒)
autoReloadDic=true #是否启用
4.创建数据库表
DROP TABLE IF EXISTS `md_es_ik_dic`;
CREATE TABLE `md_es_ik_dic` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
`word` varchar(100) DEFAULT '' COMMENT '扩展分词',
`word_type` varchar(100) DEFAULT '' COMMENT '0:扩展分词 1:停用分词 ',
`delete_type` tinyint(4) DEFAULT '' COMMENT '0表示未删除,1表示删除',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT='词库维护表';
5.es lib增加一个mysql数据库驱动文件
6.启动es测试
get请求es:http://127.0.0.1:9200/_analyze
{
"analyzer":"ik_max_word",
"text":"我是一名小正太"
}
分词结果
{
"tokens": [
{
"token": "我",
"start_offset": 0,
"end_offset": 1,
"type": "CN_CHAR",
"position": 0
},
{
"token": "是",
"start_offset": 1,
"end_offset": 2,
"type": "CN_CHAR",
"position": 1
},
{
"token": "一名",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 2
},
{
"token": "一",
"start_offset": 2,
"end_offset": 3,
"type": "TYPE_CNUM",
"position": 3
},
{
"token": "名",
"start_offset": 3,
"end_offset": 4,
"type": "COUNT",
"position": 4
},
{
"token": "小",
"start_offset": 4,
"end_offset": 5,
"type": "CN_CHAR",
"position": 5
},
{
"token": "正",
"start_offset": 5,
"end_offset": 6,
"type": "CN_CHAR",
"position": 6
},
{
"token": "太",
"start_offset": 6,
"end_offset": 7,
"type": "CN_CHAR",
"position": 7
}
]
}
如果我们需要小正太分词也分一个词在数据库新增
es日期打印
再次测试分词结果
{
"tokens": [
{
"token": "我",
"start_offset": 0,
"end_offset": 1,
"type": "CN_CHAR",
"position": 0
},
{
"token": "是",
"start_offset": 1,
"end_offset": 2,
"type": "CN_CHAR",
"position": 1
},
{
"token": "一名",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 2
},
{
"token": "一",
"start_offset": 2,
"end_offset": 3,
"type": "TYPE_CNUM",
"position": 3
},
{
"token": "名",
"start_offset": 3,
"end_offset": 4,
"type": "COUNT",
"position": 4
},
{
"token": "小正太",
"start_offset": 4,
"end_offset": 7,
"type": "CN_WORD",
"position": 5
}
]
}
可以看到小正太分成了一个词
可能遇到的问题
启动报错:Plugin [analysis-ik] was built for Elasticsearch version 6.5.0 but version 6.5.1 is running
因为要求es版本和ik版本要完全一致,可以尝试一下修改ik目录下的plugin-descriptor.properties
改成es版本
找不到数据库驱动
ikpom增加数据库驱动依赖 es lib放入数据库驱动jar
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
permission java.net.SocketPermission "ip:port", "listen,accept,connect,resolve";
修改jre下的lib/security java.policy
我的是在:/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/security
增加:
permission java.net.SocketPermission "ip:port", "listen,accept,connect,resolve";
可能会出现当前文件只读 切换为root权限修改即可
elasticsearch实战 修改IK源码实现词组动态更新的更多相关文章
- 修改FFMpeg源码—捕获丢包
概述 最近我们项目有一个需求就是解决客户端播放RTSP视频流花屏的问题,一般来说丢包就会引起花屏,导致客户端花屏的因素又有很多,比如说: 相机到服务器丢包 服务器到客户端丢包 等等... 其中服务器到 ...
- 修改spring源码重写classloader实现项目加密
(一)操作方法和spring源码添加修改部分 事先说明:spring源码要下载好,会有修改spring的源码操作,本文和本作者所依赖的spring项目的版本是3.1.1,spring4及以上源码对 ...
- 修改VCL源码实现自定义输入对话框
来自:https://yq.aliyun.com/wenji/88428 通过修改VCL源码实现自定义输入对话框 在BCB中有两个函数可以实现输入对话框:InputBox和InputQuery,其实I ...
- paramiko修改本分源码
一.获取paramiko源码 环境:Python3 下载地址:https://github.com/paramiko/paramiko 使用的是 demos这个文件夹 二.修改部分源码用以登入 2.1 ...
- 修改CAS源码是的基于DB的认证方式配置更灵活
最近在做CAS配置的时候,遇到了数据源不提供密码等数据的情况下,怎样实现密码输入认证呢? 第一步:新建Java项目,根据假面算法生成CAS加密工具 出于保密需要不提供自定义的加密工具,在您的实际项目中 ...
- 修改springfox-swagger源码,使example中时间格式默认为“yyyy-MM-dd HH:mm:ss”
修改swagger源码,使example中时间格式默认为"yyyy-MM-dd HH:mm:ss" 前言 简单点说,在swagger中,怎么能针对以下vo中的java.util.D ...
- 修改json源码支持datetime序列化
修改json源码支持datetime序列化 import json import datetime now = datetime.datetime.today() json.dumps(now) 抛出 ...
- THINKPHP_(8)_修改TP源码,支持基于多层关联的任一字段进行排序
之前博文 前述博文THINKPHP_(1)_修改TP源码,支持对中文字符串按拼音进行排序,其解决的主要问题是,对于查询出的think\collection数据,按指定字段对数据进行排序,从而在页面上进 ...
- 修改unittest源码之tearDown
需求 最近在写selenium自动化平台,想把每条用例后面都带上截图,最开始是每条用例加上封装好的截图函数,但是发现太麻烦,就决定加在tearDown函数里面,每条用例结束后执行截图操作. 那么问题来 ...
随机推荐
- Spring源代码解析和配置文件载入
Spring类的继承结构图: Spring运用了大量的模板方法模式和策略模式,所以各位看源代码的时候,务必留意,每个继承的层次都有不同的作用.然后将同样的地方抽取出来,依赖抽象将不同的处理依照不同的策 ...
- 学习笔记——WCF
学了一下WCF,发现怎么跟Web Service这么像! 这个WCF究竟干嘛的? 一查,原来: "Windows Communication Foundation (WCF) 是由微软发展的 ...
- local_response_normalization 和 batch_normalization
Normalization Normalization local_response_normalization local_response_normalization出现在论文”ImageNe ...
- B5248 [2018多省省队联测]一双木棋 状压dp
这个题当时划水,得了二十分,现在来整一整. 这个题用状压来压缩边界线,然后通过记忆化搜索进行dp.我们可以观察到,其实每次转移,就是把一个1向左移一位.最后的状态设为0. 这其中还要有一个变量来记录谁 ...
- B1001 狼抓兔子 最小割
题目描述 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的, 而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形: ...
- 判断文件是否为空 C++
#include <sys/stat.h> int stat(const char *restrict pathname, struct stat *restrict buf); stru ...
- html5 读取本地文件
尊重原创:http://hushicai.com/2014/03/29/html5-du-qu-ben-di-wen-jian.html HTML5为我们提供了一种与本地文件系统交互的标准方式:Fil ...
- C# 创建单例
private static WorkFlow instance = null; private static readonly object syncObj = new ob ...
- C - Stones on the Table
Problem description There are n stones on the table in a row, each of them can be red, green or blue ...
- CSS浮动的处理
之前已经发过一遍有关浮动的解决办法,今天看到一些资料后又有了新的想法: 在CSS布局中float属性经常会被用到,但使用float属性后会使其在普通流中脱离父容器,让人很苦恼 1 浮动带来布局的便利, ...