前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ElasticSearch自定义中文分词插件开发介绍

ElasticSearch自定义中文分词插件开发介绍

作者头像
程序猿DD
发布2022-08-31 14:16:31
8700
发布2022-08-31 14:16:31
举报
文章被收录于专栏:程序猿DD程序猿DD

作者:吴峻申

原文:http://www.wujunshen.cn/posts/279953901.html

写在前面

项目配置

  • JAVA GraalVM 17
  • ElasticSearch 8.3.3
  • Junit5 5.9.0
  • lombok 1.8.24
  • logback 1.2.11
  • hanlp汉语自然语言处理工具包 1.8.3

如何使用

获取hanlp语料

直接下载 data.zip:http://nlp.hankcs.com/download.php?file=data

后续会使用到

本地搭建nginx网站显示静态内容

快速安装nginx

以我的mac为例

代码语言:javascript
复制
brew install nginxCopy

然后根据安装成功的提示去找nginx.conf文件,我这里提示如下图

配置nginx

根据上图里提示去编辑nginx.conf文件

代码语言:javascript
复制
vi /opt/homebrew/etc/nginx/nginx.confCopy

编辑内容可以下面文件内容为准,当然读者也可以根据自己情况修改

代码语言:javascript
复制
worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       8080;
        server_name  local.wujunshen.com;

        location / {
            root   /usr/local/var/www;
            index  index.html index.htm;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

    include servers/*;
}Copy

其中特别注意的是下面这段

代码语言:javascript
复制
listen       8080;
server_name  local.wujunshen.com;

location / {
   root   /usr/local/var/www;
   index  index.html index.htm;
}Copy

首先我自定义了一个local.wujunshen.com本地域名,并且和127.0.0.1绑定在一起。然后我把前述的data.zip解压到/usr/local/var/www目录,并让root指向它

提示local.wujunshen.com绑定127.0.0.1是这样的 我先 vi /etc/hosts,打开hosts文件 然后copy 127.0.0.1 local.wujunshen.com这一行到任意位置 最后 source /etc/hosts使其生效 这样就绑定成功了

编辑nginx.conf完成后,执行

代码语言:javascript
复制
brew services restart nginxCopy

重启nginx,让其生效

静态网站内容验证

打开浏览器输入 http://local.wujunshen.com:8080/

看见下列界面,证明nginx安装成功

然后接着输入 http://local.wujunshen.com:8080/data/dictionary/custom/CustomDictionary.txt 显示如下界面

这表明nginx网站已经能显示静态内容了

插件安装

打包代码

执行

代码语言:javascript
复制
mvn clean packageCopy

注意 代码中的hanlp-hot-update.cfg.xml文件内容 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties>     <comment>HanLP 扩展配置</comment>     <!--用户可以在这里配置远程扩展字典 -->     <entry key="remote_ext_dict"> http://local.wujunshen.com:8080/data/dictionary/custom/CustomDictionary.txt     </entry>     <!--用户可以在这里配置远程扩展停止词字典-->     <entry key="remote_ext_stopwords"> http://local.wujunshen.com:8080/data/dictionary/stopwords.txt     </entry>     <!--用户可以在这里配置远程同义词字典-->     <entry key="remote_ext_synonyms"> http://local.wujunshen.com:8080/data/dictionary/synonym/CoreSynonym.txt     </entry> </properties>Copy 其中remote_ext_dict,remote_ext_stopwordsremote_ext_synonyms都是前述搭建的nginx网站静态内容

发布插件

将打包成zip格式的插件包(在/target/releases目录下)

解压到ElasticSearch下的plugins子目录下,这样就发布完成了

运行插件

重新启动ElasticSearch

在ElasticSearch目录下的bin子目录启动ElasticSearch

注意 不能用root账号启动 需要新建账号并赋权,然后启动ElasticSearch

查看插件执行结果

使用head插件,在“复合查询”里输入如下截图内容(事先创建了一个叫index-test的索引) ,并按下方“提交请求”按钮执行,可见右侧执行结果

也可在命令窗口输入下列命令执行,结果和上图是一样的。

代码语言:javascript
复制
curl -H "Content-Type:application/json" -X POST -d '{
"analyzer": "hanlp_synonym",
"text": "英特纳雄耐尔"
}' http://localhost:9200/index-test/_analyze?pretty=trueCopy

见下图命令行执行结果

开发介绍

ES分词器简单介绍

ElasticSearch默认就有标准的英文分词器。

但是对于母语是非英语的人来说,光有英文分词器是远远不够的。

因此各国家的程序员都会开发对应自己母语的分词插件来增强ElasticSearch的分词功能

不管何种自然语言的分词器,无外乎由下列三部分组成

  • 分词器(Analyzer)
  • 分解器(Tokenizer)
  • 词元过滤器(TokenFilter)

而底层依赖的都是分词算法。

本项目使用的分词算法是 HanLP ,作者何晗。

具体官网地址可见 HanLP(https://www.hanlp.com/),号称是最好的中文分词算法。

除此之外,分词器应该还具有一些附加功能,比如下列两个功能

  • 支持用户自定义字典
  • 支持字典的热更新功能

HanLP 简单介绍

HanLP 是一系列模型与算法组成的 NLP (自然语言处理) 工具包,具备功能完善、性能高效、架构清晰、语料时新、可自定义特点,详情可参考 HanLP GitHub:https://github.com/hankcs/HanLP

选择它作为本项目底层分词算法理由如下

  • Java 分词包中最流行的分词算法
  • 提供多种分词器,既可基于字典也可基于分词模型
  • 坚持使用明文字典,可借助社区力量对字典进行不断完善
  • 开发文档和代码样例丰富

项目代码结构

见下图

  • assemblies: 插件打包(plugin.xml)配置文件
  • com.wujunshen.core: 分词插件核心类
  • com.wujunshen.dictionary: 同义词字典类
  • com.wujunshen.enumation: 涉及的枚举
  • com.wujunshen.exception: 自定义异常
  • com.wujunshen.nature: 自然分词属性
  • com.wujunshen.plugin: 分词插件定义
  • com.wujunshen.update: 热词更新处理类
  • com.wujunshen.utils: 涉及的工具类
  • resources: 插件属性文件所在目录。包括插件配置、HanLP的热词更新配置、Java 安全策略、logback日志配置等文件
  • test下的com.wujunshen.entityMyAnalyzerTest: 使用JUnit5编写的单元测试方法

单元测试类介绍

具体见MyAnalyzerTest.java

其中具体说明一下私有方法 analyze

代码语言:javascript
复制
private List<Token> analyze(SegmentationType segmentationType, String text) throws IOException {
    Tokens result = new Tokens();
    List<Token> resultList = new ArrayList<>();
    Analyzer analyzer = new MyAnalyzer(segmentationType);
    TokenStream tokenStream = analyzer.tokenStream("text", text);

    tokenStream.reset();

    while (tokenStream.incrementToken()) {
        CharTermAttribute charTermAttribute = tokenStream.getAttribute(CharTermAttribute.class);
        TypeAttribute typeAttribute = tokenStream.getAttribute(TypeAttribute.class);

        OffsetAttribute offsetAttribute = tokenStream.getAttribute(OffsetAttribute.class);

        PositionIncrementAttribute positionIncrementAttribute =
                tokenStream.getAttribute(PositionIncrementAttribute.class);

        Token token = new Token();
        token.setToken(charTermAttribute.toString());
        token.setStartOffset(offsetAttribute.startOffset());
        token.setEndOffset(offsetAttribute.endOffset());
        token.setType(typeAttribute.type());
        token.setPosition(positionIncrementAttribute.getPositionIncrement());

        resultList.add(token);
    }

    tokenStream.close();

    result.setTokens(resultList);

    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    log.info("{}\n", objectMapper.writeValueAsString(result));

    return resultList;
}Copy

Analyzer 类是一个抽象类,是所有分词器基类,通过 TokenStream 类将文本转换为词汇单元流。

TokenStream 使用流程

  1. 实例化 TokenStream, 向 AttributeSource 添加属性(词汇单元文本text、位置增量position、偏移量offset、词汇类型type等)
  2. 调用 reset 方法, 将流(stream)重置到原始(clean)状态
  3. 循环调用 incrementToken 方法,处理 Attribute 属性信息
  4. 调用 close 方法释放资源

注意 由上可知 我们需要重点关注 TokenStream 的实例化、resetincrementTokenclose这几个方法实现

还需重点关注安全策略文件

plugin-security.policy 文件可见前述代码结构的图里,需要放置在 resources 目录下。

这样打包后才会在插件根目录下。

但是实际执行时,ElasticSearch的日志会报 AccessControlException错误,这个可能是远程加载自定义分词字典(见README.md文件中所述的nginx静态内容网站搭建内容) 时,需要网路连接权限。

因此我在 MyTokenizer.java 中,加入了下列代码,如果显示正常,则说明远程加载分词字典成功

代码语言:javascript
复制
static {
    SecurityManager sm = System.getSecurityManager();

    if (sm != null) {
        sm.checkPermission(new SpecialPermission());
    }

    AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
        Nature.create("auxiliary");

        return null;
    });
    AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
        nlpSegment = HanLP.newSegment()
                // 词性标注
                .enablePartOfSpeechTagging(true)
                // 计算偏移量
                .enableOffset(true)
                // 中文人名识别
                .enableNameRecognize(true)
                // 日本人名识别
                .enableJapaneseNameRecognize(true)
                // 数量词识别
                .enableNumberQuantifierRecognize(true)
                // 机构名识别
                .enableOrganizationRecognize(true)
                // 音译人名识别
                .enableTranslatedNameRecognize(true);

        indexSegment = HanLP.newSegment()
                .enableIndexMode(true)
                // 词性标注
                .enablePartOfSpeechTagging(true)
                // 计算偏移量
                .enableOffset(true);

        // 在此处显示调用一下分词,使得加载词典、缓存词典的操作可以正确执行
        log.info(String.valueOf(nlpSegment.seg("HanLP中文分词工具包!")));
        log.info(String.valueOf(indexSegment.seg("HanLP中文分词工具包!")));

        return null;
    });
}Copy

总结

本项目功能可总结为下列这些

  • 内置3种分词模式,适合不同场景(索引分词、nlp分词、同义词索引分词)
  • 支持外置字典(需要搭建nginx静态内容网站)
  • 支持分词器级别的自定义字典
  • 支持远程字典热更新

参考资料

项目源码:https://gitee.com/darkranger/hanlp-plugin

特别感谢

本项目单元测试类 ,MyAnalyzerTest.java:

https://gitee.com/darkranger/hanlp-plugin/blob/master/src/test/java/com/wujunshen/core/MyAnalyzerTest.java,所使用的文本解析内容,来源于倪匡老先生的小说: 卫斯理系列中的《透明光》第一章

老先生的具体生平可见,百度百科:https://baike.baidu.com/item/倪匡/333092

向刚去世不久的倪匡老先生致以崇高的敬意~

------

我们创建了一个高质量的技术交流群,与优秀的人在一起,自己也会优秀起来,赶紧点击加群,享受一起成长的快乐。另外,如果你最近想跳槽的话,年前我花了2周时间收集了一波大厂面经,节后准备跳槽的可以点击这里领取

推荐阅读

··································

你好,我是程序猿DD,10年开发老司机、阿里云MVP、腾讯云TVP、出过书创过业、国企4年互联网6年。从普通开发到架构师、再到合伙人。一路过来,给我最深的感受就是一定要不断学习并关注前沿。只要你能坚持下来,多思考、少抱怨、勤动手,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你看好一个事情,一定是坚持了才能看到希望,而不是看到希望才去坚持。相信我,只要坚持下来,你一定比现在更好!如果你还没什么方向,可以先关注我,这里会经常分享一些前沿资讯,帮你积累弯道超车的资本。

点击领取2022最新10000T学习资料

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-08-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿DD 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 如何使用
    • 获取hanlp语料
      • 本地搭建nginx网站显示静态内容
        • 插件安装
          • 查看插件执行结果
          • 开发介绍
            • ES分词器简单介绍
              • HanLP 简单介绍
                • 项目代码结构
                  • 单元测试类介绍
                    • 还需重点关注安全策略文件
                      • 总结
                      • 参考资料
                      • 特别感谢
                        • 推荐阅读
                        相关产品与服务
                        Elasticsearch Service
                        腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档