全国加盟服务热线
400-123-4567
首页
关于菲娱国际
菲娱动态
菲娱注册
菲娱登录
菲娱平台
菲娱APP下载
菲娱代理
菲娱国际
加盟申请
联系我们

诚信为本:市场永远在变,诚信永远不变。

菲娱动态

当前位置: 首页 > 菲娱动态

ES的同义词、扩展词、停止词热更新方案

发布时间:2024-06-10 05:45:29

最近要实现的一些功能需要让ES的同义词、扩展词、停止词能够热更新,达到让搜索更精确的目的。在网上看了很多相关的博客,现在热更新的方案已经实施成功,现在来总结一下。

ES版本:5.5.2

IK分词器版本:5.5.2

我的ES使用的中文分词器是IK分词器,IK分词器支持一种热更新的方案,部署一个web服务器,提供一个http接口,通过modified和tag两个http响应头,来提供词语的热更新。

同义词的配置,Elasticsearch自带了一个synonym同义词插件,但是该插件只能使用文件或在分析器中静态地配置同义词,如果需要添加或修改,需要修改配置文件和重启,使用方式不够友好,我需要的是热更新。

?

基于以上的现有的方案,再加上我参考了两篇博客,决定采用这样的方案:

(1)修改ik分词器源码,然后手动支持从mysql中每隔一定时间,自动加载新的词库

(2)修改一款别人自研的一个可动态维护同义词的插件,也是同样的从mysql中每隔一定时间,自动加载新的词库

(3)在项目中对相应的文档用定时任务进行重建文档操作,因为热更新的词对旧文档无效。

附上两位的博客地址:
Elasticsearch之IK分词器热更新
一个简易的Elasticsearch动态同义词插件
万分感谢两位~

好了,下面进入正题

1.下载IK分词器的源码

进入github,找到对应版本的ik分词器,下载源码,我这里是5.5.2版本的ES,所以我下载5.5.2版本的IK分词器

https://github.com/medcl/elasticsearch-analysis-ik/tree/v5.5.2
这是一个maven工程,下载下来后直接导入到eclipse中进行改造

2.修改源码

先看一下下载下来的源码的文件目录

这个-root后缀的项目就是我刚下载下来的ik的源码,这个源码上面的那个项目是我修改过后的源码。

由于我们是要动词库,所以我们直奔dic目录,找到Dictionary类,先看看它这个是怎么加载词典的。

看看他的构造函数:

initial方法:

根据上面原博主的思路,是在这个Dictionnary类中,写两个方法,用jdbc去mysql中分别查询扩展词和停止词,然后放到对应的词典中。然后再这个初始化的方法中,像原生的代码那样也调用一下这两个方法。再建立一个监控线程,定时去加载扩展词和停止词。

我在原博主的的基础上,把配置的数据库的属性初始化的时候封装到Dictionnary类中的一个属性中,包括加载的sql和监控线程隔多久进行一次扫描。这样方便进行配置。

(1)、先在Dictionnary类定义两个自己要的属性

DB_PROPERTIES属性是我们自己建的属性文件的文件名字
myProperties属性是用来把读取到的属性文件里的属性装起来

(2)、在config目录下创建db.properties文件

(3)、在构造方法中读取db.properties,并提供几个获取属性的方法

 

获取几个属性的方法

 

(3)、写两个从数据库中读取扩展词和停止词的方法,再写一个重新加载扩展词和停止词的方法给监控线程去调用

 

(4)、代码到这里差不多就改完了,但是由于我的ES原来本来已经有了一个IK分词器插件,我并不想把之前的IK分词器插件替换掉,怕影响到以前的业务,所以我还需要把这个修改了源码过后的IK分词器取个别的名字。

?进入pom.xml中修改插件名字

修改org.elasticsearch.plugin.analysis.ik.AnalysisIkPlugin类

(5)、由于我们使用了mysql来做这个词典加载,项目中使用到了mysql连接,所以要把mysql驱动包配置到plugin.xml文件中,否则打出来的包中没有mysql的驱动包,到时候会报错。加一行配置<include>mysql:mysql-connector-java</include>

(6)、接下来,mvn clean?package打包代码,然后找到 target\releases\elasticsearch-analysis-ik-5.5.2.zip

(7)、解压缩该文件到es的plugin目录下,为了避免跟之前的ik文件夹重复,最好先在别的地方解压,然后给文件夹重新命名,再复制到plugin目录下。

1.下载原博主上传到github的源码

https://github.com/ginobefun/elasticsearch-dynamic-synonym


1.1目录结构

1.2实现方式

引用原博主自己写的:

  • DynamicSynonymTokenFilter参考了SynonymTokenFilter的方式,但又予以简化,使用一个HashMap来保存同义词之间的转换关系;
  • DynamicSynonymTokenFilter只支持Solr synonyms,同时也支持expand和ignore_case参数的配置;
  • DynamicSynonymTokenFilter通过数据库来管理同义词的配置,并轮询数据库(通过version字段判断是否存在规则变化)实现同义词的动态管理;

1.3源码

1.3.1? DynamicSynonymTokenFilterFactory

1.3.2? SynonymRuleManager初始化方法

loadSynonymRule()方法就是去数据库查询相应的数据,原博主提供的有一个表结构来存同义词,每一组同义词按照相应的格式就好,ES支持两种格式,一种xxxx,xxxx,xxxx,英文逗号分隔,一种xxxx,xxxx,xxxx => xxxx格式,选择其中一种就好,源码都有做判断的。

监控线程就是重新加载数据,创建一个临时的SynonymRuleManager,加载完了数据,然后再把map赋值给原本的那个synonymMap,防止加载同义词的时候影响插件工作。跟上面IK加载扩展词什么的是一个意思。

2.修改源码

2.1需要改动的地方

  • 由于我们本身的业务已经有了自己的词库表,所以不能用源码提供的表以及sql,我需要按照自己的逻辑查出数据,然后按照格式放入synonymMap中。
  • 源码用到的db_url是通过ES的settings配置进行加载的,我为了和ik那样统一一下,所以我需要改变加载方式,同样的创建一个db.properties文件来存放要加载的属性,顺便把sql也提取出来,源码是在代码里把sql写死的。

2.2修改Configuration,添加几个我自己需要的属性

 

2.3? 在项目目录下新建config文件夹,创建db.properties文件,修改 DynamicSynonymTokenFilterFactory,加载db.properties文件

 

2.4?JDBCUtils新建一个我自己的查询数据库的方法querySynonymAiLikenessKeywords()

 

2.5?修改SynonymRuleManager.loadSynonymRule()方法和reloadSynonymRule()方法

 

?2.6 修改Monitor.run()方法,直接调用SynonymRuleManager.reloadSynonymRule()就好了

 

这样就基本上可以了,接下来打包mvn clean package,打包之前别忘了,这个插件也用了mysql,跟上文一样需要到plugin.xml中去修改配置,这里我就不贴图了。打包完成后找到target/releases/下的.zip包,在ES的plugin包下新建一个文件夹,把zip包解压到该目录。到此为止两个插件修改完毕,看看plugin目录:

?

?

由于同义词过滤器需要配置,Elasticsearch自带的同义词过滤器在分析器配置的话配置如下:

 

所以,想要使用上两个插件就需要对文档进行设置,设置它的analyzer和filter。

由于使用的ES版本问题,5.5.2版本已经不支持在elasticsearch.yml中进行,所以只能通过restfulAPI进行配置

在ES中新建一个文档,通过URL进行设置

 

设置成功后

这时候就可以使用了。

接下来还有几个问题:

(1)如果每新建一个文档,要用到这样配置的时候,都这么手动配置太麻烦了,而且几套环境,每一个都去配置,这样不太友好。

(2)重建文档问题,新加入词库的词语对于旧的文档不生效,所以需要有一个定时任务去定时重建文档。

所以接下来我就在项目中把这些实现了一下

项目是一个springboot项目,最开始使用ES的时候,为了图方便,使用的是elasticsearch spring data jpa ,用repository来操作的ES。现在要对settings进行设置,所以我想使用elasticsearchTemplate来进行设置,但是当用elasticsearchTemplate的时候就导致实体类上的注解@Field中配置的映射关系什么的都不生效了。之后网上找了一下才发现,当使用TransportClient客户端的时候会导致那个映射关系失效的。那就索性就不让jpa帮我生成文档了,生成文档的这一步由我自己来操作。

就直接设置@Document注解中的属性createIndex的值为false。

顺便提一下,由于几套环境用的是同一个ES,所以我做了一下动态indexName的操作,通过配置文件来加载索引名字。

首先在配置文件application.properties中设置一个属性

然后建立一个配置类

EsIndexNameConfig
?

 

设置一个成员变量,让这个成员变量加载application.properties中的属性

之后在@Document中用 #{esIndexNameConfig.aiRobotindexName}就能取到indexName了。

好的,接下来继续手动生成文档settings和mapping

1.建立接口类EsSettingsMap

由于考虑到以后可能还有其他文档需要自定义settings的Map,所以弄一个通用的接口类,以后再有新增的文档,就只需要实现这个接口就可以加载了。

 

2.实现接口

 

3.建立init类,实现ApplicationRunner接口,让程序在启动的时候就初始化这个设置。逻辑是先获取到EsSettingsMap下的所有实现类,我这里实现的比较简单,默认只找跟这个接口在一个包下所有实现类,所以回头建立实现类的时候只能跟这个接口放在同一目录下。获取到所有实现类后,遍历实现类,先判断这个index在ES是否存在,如果存在就不用管了(之前已经生成过了)。如果不存在,就创建索引,把settingsMap传进去。并且putMapping()一下,这个操作是使实体类上的注解@field中配置的mapping信息生效。

 

ClassUtils的getAllAssignedClass()方法

 

这样就实现了在项目中自己配置settings了。

接下来是 定时任务重建文档

springboot的定时任务比较简单,直接用注解就可以了

在启动类上,加上@EnableAsync和@EnableScheduling注解

第二个注解是开启定时任务开关,第一个注解是开启多线程定时任务,避免相同时间的定时任务有冲突。

建立定时任务类EsScheduled
?

 

逻辑也不复杂,也是遍历实现类,看看索引是否存在,如果存在就更新index中的所有记录。我设置的是每天零点过5分扫描。

?

好了,这样的话整体的方案已经实现了,通过修改ik分词器的源码和以为博主自己写的插件的源码,然后跟项目搭配,实现同义词、扩展词、停止词的热更新并尽量保证词库更新后的效果。

?

看看测试效果吧

我数据库中的表结构是这样的

 

添加几条数据

测试

返回数据

 

?可以看到,扩展词和同义词都已经生效了。

再添加一条停止词试试

先分词“你要这个干什么”

结果

 

添加停止词“什么”

测试效果

结果中没有把“什么”这个词给分出来,证明已经被添加到停止词词库了。

?

好了,这就是我这些天研究的一些乱七八糟的东西的总结,希望能对看到的你有所帮助~~~

?

?

首页 关于菲娱国际 菲娱动态 菲娱注册 菲娱登录 菲娱平台 菲娱APP下载 菲娱代理 菲娱国际 加盟申请 联系我们
版权所有:Copyright © 2012-2018 某某网站 版权所有
ICP备案编号:琼ICP备xxxxxxxx号

平台注册入口