首页 > 其他分享 >Lucene的IK分词器学习,增加支持单个特殊符号搜索

Lucene的IK分词器学习,增加支持单个特殊符号搜索

时间:2024-06-11 15:34:08浏览次数:28  
标签:cursor 分词器 CHAR Lucene IK context 特殊符号 分词 特殊字符

前言

感谢CSDN这篇文章,原始代码基于这里。

正常对于“[email protected]”这段文字,搜索'@'这个符号是搜不出来的。本文主要修改是扩展IK分词器,增加了对诸如"@ -"这种特殊文字的检索。

当然这个其实并没有多少实际意义,所以基本也是出于学习的目的。

正文

IK分词器分析

这里不深入原理,只涉及到修改时需要考虑的地方。

  • 分词逻辑:IKAnalyzer->IKTokenizer->IKSegmenter,最终实际是IKSegmenter中的next()方法对输入流进行分词,获取下一个分词(也叫词元Lexeme)。
  • IKSegementer分词:包含三个子分词器,实现ISegmenter接口,依次调用,彼此独立分词(既一段文字可以被不通分词器重复分词)
    • LetterSegmenter: 识别字母、数字、符号的组合
    • CN_QuantifierSegmenter: 识别中文量词分词,如一条三艘
    • CJKSegmenter: IK的核心,中文分词,通过从字典数据中查找匹配分词。不过不是本文的核心了。
  • 子分词器逻辑:AnalyzeContext为输入参数,存储输入流信息,子分词器解析每个字符,通过字符类型判断词元开始和结尾。当一个分词结束,通过AnalyzeContext提供的addLexeme方法增加一个分词(词元)
  • IKArbitrator歧义裁决:其实这里涉及到一个IK分词器的smart参数,表面上,配置smarttrue时,分词比较粗,为false时分词粒度更细,这个参数影响的就是这个裁决器。当拥有smarttrue时,多个分词如果彼此有覆盖关系,会自动进行合并,如热反应堆,原本会被拆分成反应``反应堆``堆``热反应堆等多个词,但是如果配置了smart,由于热反应堆本身包含了其他词,则最后分词结果只会有一个热反应堆词元。所以一般来说,建立索引的时候,不配置smart,而搜索前分词的时候,可以配置smart。有点扯远了。

修改说明

核心思路:在原有的分词器中,创建一个信息的分词器SpecialCharSegmenter,当碰到特殊字符时,插入一个新的Lexeme

由于原始的几个类IKAnalyzer IKTokenizer IKSegmenter等都是final的,所以我直接单独创建了类MyIKAnalyzer,MyIKTokenizer,MyIKSegmenter替代这几个类。

比较重要的修改点说明如下:

MyCharacterUtil

扩展原有CharacterUtil工具类,增加特殊字符类型值,在解析字符流时,识别该类型

 //需要进行索引的特殊字符
    public static final int CHAR_SPECIAL = 0X00000010;

    //需要进行索引的特殊字符,可以扩充修改
    private static final char[] Letter_Special = new char[]{'#', '-', '@', '.'};

    static int identifyCharType(char input) {
           .......省略非关键部分
            } else if (isSpecialChar(input)) {
                return CHAR_SPECIAL;
            }
    }

    //特殊字符的判断
    static boolean isSpecialChar(char input) {
        int index = Arrays.binarySearch(Letter_Special, input);
        return index >= 0;
    }

MyAnalyzeContext

读取字符流的工具类,原本调用CharacterUtil,修改为调用我们自定义的MyCharacterUtil

    /**
     * 初始化buff指针,处理第一个字符
     */
    void initCursor() {
        this.cursor = 0;
        this.segmentBuff[this.cursor] = CharacterUtil.regularize(this.segmentBuff[this.cursor]);
        //修改点
        this.charTypes[this.cursor] = MyCharacterUtil.identifyCharType(this.segmentBuff[this.cursor]);
    }

    /**
     * 指针+1 成功返回 true; 指针已经到了buff尾部,不能前进,返回false 并处理当前字符
     */
    boolean moveCursor() {
        if (this.cursor < this.available - 1) {
            this.cursor++;
            this.segmentBuff[this.cursor] = CharacterUtil.regularize(this.segmentBuff[this.cursor]);
            //修改点
            this.charTypes[this.cursor] = MyCharacterUtil.identifyCharType(this.segmentBuff[this.cursor]);
            return true;
        } else {
            return false;
        }
    }

MyIKSegmenter

loadSegmenters中,加载自定义的分词器

        // 处理特殊字符的子分词器
        segmenters.add(new SpecialCharSegmenter());

SpecialCharSegmenter

核心是碰到特殊字符,增加一个词元,逻辑非常简单。

    public void analyze(AnalyzeContext context) {
        if (MyCharacterUtil.CHAR_SPECIAL == context.getCurrentCharType()) {
            // 遇到特殊字符,输出词元
            Lexeme newLexeme = new Lexeme(context.getBufferOffset(), context.getCursor(), 1,
                    Lexeme.TYPE_LETTER);
            context.addLexeme(newLexeme);
        }
    }

CustomLetterSegmenter

扩展原有的LetterSegmenter,之所以要修改,是发现该类会将部分特殊符号,如[email protected]识别为一个整体类,而识别的方式是通过字符类型为CHAR_USELESS

当我修改了字符解析时,为特殊符号定义了新的类型CHAR_SPECIAL,所以需要修改processMixLetter:

//原始
        } else if ((CharacterUtil.CHAR_USELESS == context.getCurrentCharType())
                    && this.isLetterConnector(context.getCurrentChar())) {
                // 记录下可能的结束位置
                this.end = context.getCursor();
            } else {

//修改为
        } else if ((CharacterUtil.CHAR_USELESS == context.getCurrentCharType()
                    //可能被修改为了特殊字符类型
                    || MyCharacterUtil.CHAR_SPECIAL == context.getCurrentCharType())
                    && this.isLetterConnector(context.getCurrentChar())) {
                // 记录下可能的结束位置
                this.end = context.getCursor();
            } else {

验证

代码上传在 github https://github.com/mosakashaka/wbdemos/tree/master/lucene-ik-demo

直接运行后通过地址http://localhost:8080/lucene/createIndex,创建普通索引(不支持符号),索引中自动添加了几条数据,其中包含了@符号。

此时通过搜索接口http://localhost:8080/test/searchText?text=@检索,搜索不到数据。

如果通过http://localhost:8080/lucene/createIndex2(地址后多了2),则会创建支持符号的索引,此时搜索@ -等符号则可以搜索到。

结语

本身可能意义不大,纯粹是学习的目的。

IK原始的代码看起来很久了,我看了下ES用的那个IK分词器的提交记录,16年以后几乎也没有修改。


标签:cursor,分词器,CHAR,Lucene,IK,context,特殊符号,分词,特殊字符
From: https://www.cnblogs.com/mosakashaka/p/18242152

相关文章

  • 使用Druid替换springboot默认连接池HikariPool
    使用Druid替换springboot默认连接池HikariPool1.在pom文件中增加Druid依赖<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.23</version></dependency>2.在a......
  • Android studio实现图标方式展示信息(折线图等)时com.github.mikephil.charting.charts.
    参考连接——https://blog.csdn.net/m0_37919094/article/details/122416408 去https://gitcode.com/PhilJay/MPAndroidChart/overview?utm_source=csdn_github_accelerator&isLogin=1下载MPAndroidChart的zip包,解压 在Androidstudio中操作file——》new——》importm......
  • csrf与xss差别 别在弄乱了 直接靶场实操pikachu的csrf题
    我们现在来说说这2个之间的关系,因为昨天的我也没有弄清楚这2者的关系,总感觉迷迷糊糊的。xss这个漏洞是大家并不怎么陌生,导致xss漏洞的产生是服务器没有对用户提交数据过滤不严格,导致浏览器把用户输入的当作js代码返回客户端进行执行,这样就导致了攻击。xss(跨站脚本攻击)可以......
  • Tiktok机房ip好还是住宅ip好?
    住宅ip比较好,机房数据中心代理高效、低价,所以使用的人多且用处复杂,这类ip极大可能存在滥用的黑历史,通过此类ip访问tiktok,被禁止的可能性更高,更容易被拉入黑名单。所以我们推荐tiktok独享原生ip搭建节点。而住宅ip是isp还是adsl提供商分配给个人用户的实际家庭住宅ip地址,由100%......
  • Tiktok机房ip好还是住宅ip好?
    住宅ip比较好,机房数据中心代理高效、低价,所以使用的人多且用处复杂,这类ip极大可能存在滥用的黑历史,通过此类ip访问tiktok,被禁止的可能性更高,更容易被拉入黑名单。所以我们推荐tiktok独享原生ip搭建节点。而住宅ip是isp还是adsl提供商分配给个人用户的实际家庭住宅ip地址,由100%......
  • Cobalt Strike使用小记
    环境设置攻击机KaliLinux:172.24.4.7跳板机Windows10:172.24.4.22目标机Windows7:172.24.4.35Windows7作为目标机。启动CS服务端首先在Kali服务端启动CS,配置如下:IP:Kali的IP密码:demo(可以随意,但要记住)连接CS服务端在Windows10上启动CS客户端并连接......
  • pikachu靶场(File Inclusion(文件包含)通关教程)
    1.FileInclusion(local)本地文件包含1.1打开网站,发现有个下拉框,随便选择一个,然后点击提交1.2发现图中有个参数变了,其他的也会变,猜测这里可能有其他隐藏的文件1.3直接进行抓包 ,右键发送到爆破模块1.4 选择爆破方式为sniper,选择爆破点 1.5设置攻击字典,选择数字,从1......
  • 2种方法解决需要clik点击数的题目——[HNCTF 2022 WEEK2]getflag 137分 MFC patch RE
    题目   DIEIDA找到判断点击数的if,我们修改一下汇编指令让点击数<99999999就成立这个程序没有要求我们输入,说明flag是程序打印的IDA动调 下一个断点修改 得到flag  还有一种更快的方法——CheatEngine 随便点击几次 在CE中修改点击次数 Getf......
  • 界面控件Telerik UI for WPF中文教程 - 用RadSvgImage升级应用程序UI
    TelerikUIforWPF拥有超过100个控件来创建美观、高性能的桌面应用程序,同时还能快速构建企业级办公WPF应用程序。UIforWPF支持MVVM、触摸等,创建的应用程序可靠且结构良好,非常容易维护,其直观的API将无缝地集成VisualStudio工具箱中。TelerikUIforWPF中的RadSvgImage组件使......
  • 国际版 抖音tiktok实战课程,向海外抖音出发(15节课)
    亲爱的朋友们,你们是否梦想着让自己的创意在全球舞台上闪耀?是否想要在国际版抖音TikTok上建立自己的影响力?那么,你来对地方了!我们的TikTok实战课程将带你启程,从基础到进阶,一步步解锁海外抖音的无限可能。准备好了吗?让我们携手踏上这场充满乐趣和挑战的旅程!课程概览:第1课:点......