前言:ES版本差异较大,建议跨版本的同学,可以先了解一下版本区别,建议不要跨版本使用插件或者进行项目调试。

本总结主要基于6.x版本的6.5.1(6.2.2实测可用),分词器为IK,下载地址:https://github.com/medcl/elasticsearch-analysis-ik

不做ES入门普及,直入正题。

ES操作系统:win10(如ES部署在linux,相应操作需调整)


正题

请先阅读官方热更新文档:

热更新IK分词使用方法
目前该插件支持热更新IK分词,通过上文在IK配置文件中提到的如下配置<!-用户可以在这里配置远程扩展字典-> <输入 密钥 = “ remote_ext_dict ” >位置</ entry ><!-用户可以在这里配置远程扩展停止字典-> <输入 密钥 = “ remote_ext_stopwords “ >位置</ entry >
其中location是指一个网址,其中http://yoursite.com/getCustomDict,该请求只需满足以下两点即可完成分词热更新。该http请求需要返回两个头部(header),一个是Last-Modified,一个是ETag,这两个都是字符串类型,只要有一个发生变化,
该插件就会去抓取新的分词套筒更新词库。该http请求返回的内容格式是一行一个分词,换行符用\n即可。满足上面两点要求就可以实现热更新分词了,不需要重启ES实例。可以将需自动更新的热词放在一个UTF-8编码的.txt文件里,放在nginx或其他简易http服务器下,当.txt文件修改时,
http服务器会在客户端请求该文件时自动返回相应的Last-Modified和ETag。可以另外做一个工具来从业务系统提取相关词汇,
并更新这个.txt文件。

注意:需要远程热加载,需要放开对应的远程访问安全策略

在java安装的jre/lib/security 下的java.policy文件内,

grant中添加一行
permission java.net.SocketPermission “*”, “connect,resolve”;   或者将*改为需要放开的host:port

非常重要,否则ES将无权限访问远程服务器

停用词(停止词)

由官方文档可知,对于停用词,我们可以直接在IK的配置文件ik/config/IKAnalyzer.cfg.xml配置

词典内容格式:

停用词远程词典文件热加载:

重启ES:这里为了看到ES加载停用词词典的效果,cmd默认编码为unicode,需要改为utf-8,

在ES/bin/ 内进入控制台,使用chcp 65001 进入utf-8界面,然后elasticsearch.bat启动ES。

成功启动ES后,ES会加载一次词典,并把词典内容展示

到此停用词热加载词典文件加载成功。

同义词远程词典热加载

需要安装同义词插件

地址:https://github.com/bells/elasticsearch-analysis-dynamic-synonym

但是现在正式的同义词插件是没有6.x版本的,所以我是通过修改5.x低版本的同义词插件到6.x的。

6.x版本同义词插件提供:

扫码或搜索‘程序员修炼宝典’关注公众号   回复:同义词插件

具体的修改方法:

https://blog.csdn.net/like_java_/article/details/107381018

下载完插件后,在ES/plugin下新建文件夹:dynamic-synonym,并将下载的插件解压到这个文件夹内,注意不要将插件压缩文件留在该文件夹内。

修改同义词插件的配置文件:plugin-descriptor.properties

elasticsearch.version=6.x   将版本修改为自己ES的对应版本,另外注释掉jvm site isolate 否则启动报错

Unknown properties in plugin descriptor: [jvm, site, isolated]

  • description:插件的描述信息,用来描述该插件的作用
  • version:插件的版本信息
  • name:插件在elasticsearch plugin中显示的名称
  • classname:插件的入口,需要实现Iplugin接口
  • java.version:插件采用的java版本信息
  • elasticsearch.version:插件发布到elasticsearch的那个特定版本上
  • 可选属性(作用暂时未知)
    • site:true表示发布为网站形式,_site目录下的内容将会起作用。
    • jvm:true表示设置的classname对应的类将会被加载,对于依赖的资源,配置等信息也需要打包成jar
    • isolation:如果插件应该有自己的类加载器,则为真。传递false是不赞成的,它只用于支持插件相互之间有很强的依赖性。如果这是如果不指定,则默认隔离插件。

重启ES,成功启动则说明同义词插件已载入ES。

ES同义词热加载的实现:

同义词远程加载需要在创建索引时加入:

PUT /test_remote_index
{
"settings":{"index" : {"analysis" : {"analyzer" : {"synonym" : {"tokenizer" : "whitespace","filter" : ["remote_synonym"]}},"filter" : {"remote_synonym" : {"type" : "dynamic_synonym","synonyms_path" : "http://127.0.0.1:9090/synonyms.txt", //在这里改为你的远程同义词词典即可"interval": 30},"local_synonym" : {"type" : "dynamic_synonym","synonyms_path" : "synonym.txt"}}}}
}
}

创建完索引后,在该索引内所有的数据,均可通过同义词词典进行同义词查询增强。

同义词词典这里采用的是同级同义词:

加载远程词典进行ES同义词、停用词热更新的问题:

1.热更新时效问题:

阅读官方文档可知,ES的更新取决于获取远程词典的lastmodify和Etag;由于我的词典是放在tomcat服务器中的,tomcat服务器在远程调用响应中是没有ETag属性的,所以ES只能根据lastmodify来进行更新,然而lastmodify只精确到分钟,一旦在同一分钟内多次调整词典,ES是监控不到词典变化,影响词典的及时热更新。

2.词典动态配置问题

由于词典以文件的方式保存在服务器中,如果想要实现词典的修改,是非常麻烦的,所以实现远程词典的动态配置是必要的。

3.更新同义词、停用词ES历史数据未生效问题

在成功配置停用词、同义词后,使用中发现一个问题,在ES更新停用词后,之前已载入ES的数据,依然是按照旧停用词词典进行的分词。ES更新同义词词典后,之前已建好的索引内的数据,查询时仍然按照旧的同义词词典进行。因此,需要使ES历史数据动态更新。

问题解决方案:

1.热更新时效问题解决方案:

通过阅读官方文档,我们知道ES停用词远程热加载配置,有另一种方法即引入API。

通过API,响应ETag和lastModify即可满足ES及时更新。

<entry key="remote_ext_stopwords">http://host:port/goods-search/goodsSearchRuleController/getDictionary?path=文件路径</entry>

因此需要一个后端接口如下:

/*** ES调用,实现词典热更新API from File* Description: <br> *  * @author author<br>* @taskId <br>* @param request* @param response* @return <br>*/@RequestMapping(value = "/getDictionary", method = {RequestMethod.HEAD, RequestMethod.POST, RequestMethod.GET})public String getDictionary(HttpServletRequest request, HttpServletResponse response) {DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// es最后更新词库时间LocalDateTime esDictionaryLastUpdateTime = LocalDateTime.now();cacheUtil.setValue("searchrule", "esDictionaryLastUpdateTime", esDictionaryLastUpdateTime.format(dateTimeFormatter));logger.info("===================开始进行ES热更新 {}===============", esDictionaryLastUpdateTime);try {String path = request.getParameter("path");File file = new File(path);// 词典最后更新时间LocalDateTime fileLastModifiedTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(file.lastModified()), ZoneId.systemDefault());cacheUtil.setValue("searchrule", "fileLastModifiedTime", fileLastModifiedTime.format(dateTimeFormatter));InputStream fileInput = new FileInputStream(file);BufferedReader read = new BufferedReader(new InputStreamReader(fileInput, "utf-8"));StringBuffer buff = new StringBuffer();String str = null;while ((str = read.readLine()) != null) {buff.append(str + "\n");}String content = buff.toString().trim();read.close();fileInput.close();response.addHeader("ETag", MD5Util.encryption(content));response.addHeader("Last-Modified", file.lastModified() + "");return content;}catch (Exception e) {logger.error("ES实时热更新词典异常!", e.getMessage(), e);response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();}}

lastmodify为词典文件的最后更新时间,ETag为文件内容的md5值。

2.词典动态配置问题解决方案

既然通过API可以实现ES热更新词典,响应也是通过response直接返回词典内容。

那为了实现词典的可动态配置。我们也可以直接将词典放入数据库,在这里从数据库读取也可以。

<entry key="remote_ext_stopwords">http://host:port/goods-search/goodsSearchRuleController/getDictionaryFromDB?analysisType=1</entry>
/*** ES调用,实现词典热更新API from DB* Description: <br> *  * @author author<br>* @taskId <br>* @param request* @param response* @return <br>*/@RequestMapping(value = "/getDictionaryFromDB", method = {RequestMethod.HEAD, RequestMethod.POST, RequestMethod.GET})public String getDictionaryFromDB(HttpServletRequest request, HttpServletResponse response) {DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// es最后更新词库时间   记录时间是为了解决ES历史数据更新的问题LocalDateTime esDictionaryLastUpdateTime = LocalDateTime.now();cacheUtil.setValue("searchrule", "esDictionaryLastUpdateTime", esDictionaryLastUpdateTime.format(dateTimeFormatter));String analysisType = request.getParameter("analysisType");//1表示停用词 2表示同义词if (analysisType == null || (!"1".equals(analysisType) && !"2".equals(analysisType))) {logger.error("参数不正确!");response.setStatus(HttpStatus.PRECONDITION_FAILED.value());return "analysisType incorrect";}logger.info("===================开始进行ES {}热更新 {}===============", analysisType, esDictionaryLastUpdateTime);try {FilterWordContentResponse contentResponse = searchRuleService.getDictionaryFromDB(analysisType);if (contentResponse == null) {logger.warn("数据库暂无可用词典!");response.addHeader("ETag", MD5Util.encryption(""));response.addHeader("Last-Modified", new Date(0) + "");return "";}response.addHeader("ETag", MD5Util.encryption(contentResponse.getContent()));response.addHeader("Last-Modified", contentResponse.getLastModify() + "");return contentResponse.getContent();}catch (Exception e) {logger.error("ES实时热更新词典异常!", e.getMessage(), e);response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();}}@Overridepublic FilterWordContentResponse getDictionaryFromDB(String analysisType) {String content = null;Date lastModify = null;SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");FilterWordContentResponse contentResponse = new FilterWordContentResponse();if ("1".equals(analysisType)) {List<String> stopwords = searchFilterWordMapper.selectByTypeAndStatus();lastModify = searchFilterWordMapper.selectStopwordLastModify();content = analysisList(stopwords);if (content == null) {return null;}cacheUtil.setValue("searchrule", "stopwordLastModifiedTime", dateFormat.format(lastModify));}else if ("2".equals(analysisType)) {List<SynonymWordsResponse> synonymList = searchFilterWordMapper.selectSynonymWords();lastModify = searchFilterWordMapper.selectSynonymLastModify();content = analysisList(synonymList);if (content == null) {return null;}cacheUtil.setValue("searchrule", "synonymwordLastModifiedTime", dateFormat.format(lastModify));}contentResponse.setContent(content);contentResponse.setLastModify(lastModify);return contentResponse;}

Etag为读出数据库词典内容的md5值,lastmodify是数据库内词典的最新更新时间。

3.更新同义词、停用词ES历史数据未生效问题解决方案

ES自身带有一个更新历史索引的API,updateByQuery,可按正则更新目标索引。

http://127.0.0.1:9200/*/_update_by_query?conflicts=proceed

因此,我们可通过springboot调用该API实现ES热更新词典后历史数据的更新。

该更新历史数据暂时设置为定时更新,为了不造成ES和工程系统压力,我们只需ES在停用词、同义词词库有变动并成功更新后执行一次历史数据更新。

细心地同学可能注意到了,我在解决热更新时效问题的代码中记录的三个时间:

词库最后的更新时间,ES加载词库的更新时间,ES历史数据的更新时间

通过这三个时间确保updateByQuery在每次热加载后只执行一次。

代码如下:

/*** 更新索引历史数据,用于同义词及停用词典热更新后,ES历史数据的新规则同步* 未进行速率测试* Description: <br> *  * @author <br>* @taskId <br>* @return <br>*/@PostMapping("/updateByQuery")public BssResult<?> updateByQuery(){try {LocalDateTime oldesLastUpdateByQueryTime = null;LocalDateTime wordLastModifiedTime = null;DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String esLastUpdateByQueryTimeStr = cacheUtil.getValue("searchrule", "esLastUpdateByQueryTime");String stopwordLastModifiedTimeStr = cacheUtil.getValue("searchrule", "stopwordLastModifiedTime");String synonymwordLastModifiedTimeStr = cacheUtil.getValue("searchrule", "synonymwordLastModifiedTime");String esDictionaryLastUpdateTimeStr = cacheUtil.getValue("searchrule", "esDictionaryLastUpdateTime");if (esLastUpdateByQueryTimeStr == null) {logger.info("无更新ES历史数据记录! 需要进行更新~");}else {oldesLastUpdateByQueryTime = LocalDateTime.parse(esLastUpdateByQueryTimeStr,dateTimeFormatter);}if (stopwordLastModifiedTimeStr == null || synonymwordLastModifiedTimeStr == null) {return new BssResult<>(BusiCodeEnum.SYSTEM_ERROR, "文件最后修改时间为空!");}LocalDateTime stopwordLastModifiedTime = LocalDateTime.parse(stopwordLastModifiedTimeStr,dateTimeFormatter);LocalDateTime synonymwordLastModifiedTime = LocalDateTime.parse(synonymwordLastModifiedTimeStr,dateTimeFormatter);wordLastModifiedTime = stopwordLastModifiedTime.isBefore(synonymwordLastModifiedTime) ? synonymwordLastModifiedTime : stopwordLastModifiedTime;if (esDictionaryLastUpdateTimeStr == null) {return new BssResult<>(BusiCodeEnum.SYSTEM_ERROR, "ES最新加载词典时间为空!");}LocalDateTime esDictionaryLastUpdateTime = LocalDateTime.parse(esDictionaryLastUpdateTimeStr,dateTimeFormatter);if ((oldesLastUpdateByQueryTime == null || oldesLastUpdateByQueryTime.isBefore(wordLastModifiedTime))) {//表示未按照最新词典更新历史数据while (true) {if (esDictionaryLastUpdateTime.isAfter(wordLastModifiedTime)) { //es最后更新词库时间在文件最后修改后,说明最新词典已加载到es//最后更新es历史数据时间  历史数据只在ES更新词库后更新一次LocalDateTime esLastUpdateByQueryTime = LocalDateTime.now();cacheUtil.setValue("searchrule", "esLastUpdateByQueryTime", esLastUpdateByQueryTime.format(dateTimeFormatter));return searchRuleService.updateByQuery();}else {logger.warn("ES未加载最新词典,一秒后重试~");Thread.sleep(1000);}}}else {return new BssResult<>(BusiCodeEnum.SUCCESS, "无需更新!词典最后更新时间【"+wordLastModifiedTime+"】,ES最后更新词典时间【"+esDictionaryLastUpdateTime+"】,ES索引最后更新时间【"+oldesLastUpdateByQueryTime+"】");}} catch (UnknownHostException e) {logger.error("ES历史数据更新异常!", e.getMessage(), e);return new BssResult<>(BusiCodeEnum.SYSTEM_ERROR);}catch (Exception e) {logger.error("ES历史数据更新异常!", e.getMessage(), e);return new BssResult<>(BusiCodeEnum.SYSTEM_ERROR);}}@Overridepublic BssResult<?> updateByQuery() throws UnknownHostException {TransportClient client = searchStrategyUtil.getTransportClient();logger.info("连接es成功--------------------------");UpdateByQueryRequestBuilder updateByQuery = UpdateByQueryAction.INSTANCE.newRequestBuilder(client);updateByQuery.source("*").abortOnVersionConflict(false);BulkByScrollResponse response = updateByQuery.get();logger.info("ES历史数据更新成功----------------");return new BssResult<>(BusiCodeEnum.SUCCESS, response.getUpdated());}

这里需要注意的是,ES的版本区别,导致updateByQuery调用方式不同,这里采用的是tcp连接方式来执行的更新,跳槽tcp的端口注意一下与http是不同的。

 /*** transportClient http方式连接* Description: <br> *  * @author <br>* @taskId <br>* @return* @throws UnknownHostException <br>*/public TransportClient getTransportClient() throws UnknownHostException {Settings settings = Settings.builder().put("cluster.name", "es").put("client.transport.sniff", true) //自动翻译为节点树.put("client.transport.ping_timeout", "60s").put("client.transport.nodes_sampler_interval","20s").build();;try {/* TransportAddress master = new TransportAddress(InetAddress.getByName(host), httpPort);TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(master);//单机版   */TransportClient client = new PreBuiltTransportClient(settings).addTransportAddresses(new TransportAddress(InetAddress.getByName(localHost1), localHttpPort1), new TransportAddress(InetAddress.getByName(localHost2), localHttpPort2), new TransportAddress(InetAddress.getByName(localHost3), localHttpPort3));return client;}catch (UnknownHostException e) {throw new UnknownHostException();}}

如果有什么问题或优化建议,希望能留言,共同学习进步,谢阅!

springboot基于Elasticsearch6.x版本进行ES同义词、停用词(停止词)插件配置,远程词典热加载及数据库词典热加载总结,es停用词热更新,es同义词热更新相关推荐

  1. 全新SpringBoot整合Elasticsearch6.xxx搜索引擎实战

    SpringBoot整合Elasticsearch6.xxx搜索引擎实战 一.导入相关依赖包 二.配置application.properties文件 三.ES存储实体类及注解 1.测试创建索引 四. ...

  2. Elasticsearch 同义词(dynamic-synonym插件)远程热词更新

    文章目录 Elasticsearch 同义词(dynamic-synonym)远程热词更新 零.版本说明 一.同义词本地文件读取方式(可不用插件) 1.添加同义词文件 2.创建索引,并配置同义词过滤 ...

  3. 用python批量更新es数据根据id_Python Elasticsearch批量操作客户端

    基于Python实现的Elasticsearch批量操作客户端 by:授客 QQ:1033553122 1.代码用途 Elasticsearch客户端,目的在于实现批量操作,如下: <1> ...

  4. ELK系列(十)、ES中文分词器IK插件安装和配置远程词库热加载

    简介 IK Analyzer是一个开源的,基于Java语言开发的轻量级的中文分词工具包: 最初,它是以开源项目Luence 为应用主体的,结合词典分词和文法分析算法的中文分词组件:从 3.0 版本开始 ...

  5. ElasticSearch6.x版本的SpringBoot增删改查操作和ElasticSearch6.x版本的过滤查询

    文章目录 一.教学讲解视频 二.SpringBoot操作ElasticSearch前期准备工作 三.SpringBoot增删改查ElasticSearch 1.新增修改 2.删除 3.查询 四.Ela ...

  6. native react 更新机制_react-native热更新全方位讲解

    最近在研究热更新技术,看了网上各个大佬的博客,整体流程上总是卡壳.跳了几天坑,刚刚终于把简单的热更新流程跑通,现在也正在一边学习更新,一边整理资料,在此篇博客上记录操作流程,希望我的实践能帮助各位同行 ...

  7. 【热更新】游戏热更新方案

    游戏热更新方案 热更新演化 热更新方案 [1] 进程切换 1.1 利用fork.exec切换 1.2 利用网关切换 1.3 微服务 - 进程切换注意要点 [2] 动态库替换 [3] 脚本语言热更新 热 ...

  8. ik分词器的热词更新_ik与拼音分词器,拓展热词/停止词库

    说明:本篇文章讲述elasticsearch分词器插件的安装,热词库停止词库的拓展,文章后面提到elasticsearch ,都是以es简称. 以下分词器的安装以ik分词器和pinyin分词器为例说明 ...

  9. uniapp 热更新和整包更新

    uniapp 热更新和整包更新 版本校验接口返回 自动更新 自动下载APK并安装 弹出下载APK手动安装 参考资料 版本校验接口返回 https://192.168.1.113/public/mobi ...

最新文章

  1. mysql 查询数据 程序_MySQL 查询数据
  2. vue cli 4 多环境_Vue 前端uni-app多环境配置部署服务器的问题
  3. smem – Linux 内存监视软件
  4. Linux 如何取进程运行时间,linux -- 获取进程执行时间
  5. 大数据在金融领域的应用及问题时
  6. 班扎古鲁白玛的沉默(见与不见)
  7. dockerfile COPY
  8. [转载] python __import__ 搜索路径详解
  9. 11.7动手动脑作业
  10. Atitit 数据库结果集映射 ResultSetHandler 目录 1. 常见的四种配置ResultSetHandler 1 2. Dbutil 1 3. Mybatis 致敬 3 4. H
  11. “鬼影”浅析 - 反病毒,信息安全,网络安全,反木马,病毒资讯平台,安全解决方案,电脑使用技巧,杀毒软件交流,anti-virus,民间反病毒联盟
  12. 自媒体原创检测工具,了解了这个离收获大量粉丝不会远啦!
  13. 当天剩余时间,当月剩余时间(秒数),用于redis设置过期时间
  14. 回调函数,监听函数 关系 个人学习理解
  15. 值得一生珍藏的经典台词
  16. 华为云数据库助力微鲤科技智能升级
  17. cs231n学习笔记——图像分类
  18. IDEA:IDEA中文翻译插件的安装与使用
  19. python期末试卷 答案_《python》期末考试卷A及答案
  20. 使用 Enigma Virtual Box 打包单文件

热门文章

  1. 图像的指纹——从自然图片到GAN
  2. 微信V3接口商家转账到零钱
  3. Referrer还是Referer? 一个迷人的错误
  4. 开源项目——小Q聊天机器人V1.1
  5. 使用HSqlDB的SQL/JRT功能
  6. 【蓝桥杯练习--递归】费解的开关
  7. 远程桌面启动matlab
  8. inter-class 和 intra-class的异同
  9. appcrash事件怎么解决?三种方法教你
  10. amCharts 5.2.2 Crack