ElasticSearch学习笔记-第五章 ES文档分析详解

五 文档分析

在Elasticsearch中,通过分析器来对文档的文本内容进行分析,将其中的文本内容进行分词化和规范化,这个过程被称作文档分析

5.1 什么是分析器

在Elasticsearch中,分析器是一个软件模块,它主要负责两个功能:Tokenization(分词化)和Normalization(规范化)。

  • Tokenization

    Tokenization,也即分词化是将句子拆分成单个单词的过程,它遵循一定的规则。

  • Normalization

    Normalization,也即规范化是以词干提取同义词停用词其他特征的形式对分词(词)进行处理、转换、修改和丰富的过程。

分析器由一个分词器(tokenizers)、零个或多个字符过滤器(character filters)、零个或多个**词项过滤器(token filters)**组成,其结构图如下图所示。

在这里插入图片描述

  • 字符过滤器

    对输入的文本内容进行处理,应用于字符级别。

  • 分词器

    对于被字符过滤器处理后的字符流进行进一步处理,将文本按照规则划分成若干词项。

  • 词项过滤器

    对于被分词器切分的词项进行进一步处理。

字符过滤器、分词器、词项过滤器会在后续章节进行详细介绍。

  • 分析器工作的整体流程
    1. 文本内容先要经过字符过滤器的过滤和处理
    2. 分词器对第一步处理的文本内容按照指定规则进行处理,将其拆分成若干词项。
    3. 对第二步拆分出来的词项做进一步处理
    4. 将第三步处理好的词项添加到倒排索引中。

5.2 字符过滤器

5.2.1 作用

字符过滤器应用于字符级别,文本的每个字符都会按照顺序通过这些过滤器,这些过滤器可以执行以下特定功能:

  • 从输入流中删除不需要的字符

    例如,可以从输入文本中清除

    、、 等 HTML 标记。

  • 添加或替换现有流中的其他字符

    例如,希腊字母与等效的英语单词(0和1替换成false和true)。

Elasticsearch 提供了三个字符过滤器,我们可以使用它们构建自己的定制分析器。

5.2.2 html_strip 字符过滤器

  • 作用

    html_strip 字符过滤器去除像 这样的 HTML 元素并解码像 & 这样的 HTML 实体。

  • 示例

    使用html_strip 字符过滤器处理下列文本

    <h1>html_strip & test hellow</h1>
    

    文本经过该字符过滤器后,只剩下文本"html_strip & test hellow"。我们可以发送get/post请求到http://localhost:9200/_analyze,并且在请求体中加入下列参数

    {
          
          
        "text":"<h1>html_strip & test hellow</h1>", // 要解析的文本内容
        "tokenizer": "standard",                   // 使用的分词器是标准分词器,后面小节会进行讲解
        "char_filter": ["html_strip"]               // 使用的字符过滤器
    }
    

    可以得到结果如下:

    在这里插入图片描述

    // 返回值
    {
          
          
        "tokens": [
            {
          
          
                "token": "html_strip",
                "start_offset": 4,
                "end_offset": 14,
                "type": "<ALPHANUM>",
                "position": 0
            },
            {
          
          
                "token": "test",
                "start_offset": 17,
                "end_offset": 21,
                "type": "<ALPHANUM>",
                "position": 1
            },
            {
          
          
                "token": "hellow",
                "start_offset": 22,
                "end_offset": 28,
                "type": "<ALPHANUM>",
                "position": 2
            }
        ]
    }
    

    上面的返回值可以看到,经过字符过滤器html_strip和标准分词器的处理后,Elasticsearch将输入的文本内容拆分成了三个词,HTML元素已经从里面被去除了。

    标准分词器会按照单个单词来拆分文本内容,并忽略一部分特殊字符如&,后面会进行讲解。

  • 保留部分HTML元素

    使用html_strip字符过滤器会将文本内容中的所有HTML元素去除掉,与此同时,html_strip字符过滤器支持保留部分HTML元素。我们可以使用html_strip字符过滤器的扩展属性escaped_tags来指定需要保留的HTML元素。

    例如,我们输入下列文本

    <pre><h1>html_strip & test hellow</h1></pre>
    

    我们想要文本内容在通过html_strip字符过滤器后,剩余下列文本

    <pre>html_strip & test hellow</pre>
    

    这时,我们就无法再简单的使用html_strip字符过滤器了,我们需要自定义一个分析器。(后续会详细讲解各个参数,这里我们主要关注html_strip字符过滤器的扩展属性escaped_tags即可)

    创建使用自定义分析器的索引

    // PUT http://localhost:9200/character-filter-test
    // body内容
    {
          
          
    	"settings": {
          
          
    		"analysis": {
          
          
    			"analyzer": {
          
           // 当前索引中的分析器集合
    				"test_html_strip_filter_analyzer": {
          
             // 自定义的分析器名称
    					"tokenizer": "keyword",
    					"char_filter": ["my_html_strip_filter"]  // 自定义的分析器使用的字符过滤器集合
    				}
    			},
    			"char_filter": {
          
            // 当前索引中的字符过滤器集合
    				"my_html_strip_filter": {
          
           // 自定义字符过滤器的名称
    					"type": "html_strip",  // 字符过滤器类型为html_strip
    					"escaped_tags": ["pre"]  // 要保留的html元素名
    				}
    			}
    		}
    	}
    }
    

    使用自定义分析器分析文本

    // POST http://localhost:9200/character-filter-test/_analyze
    // body内容
    {
        "text":"<pre><h1>html_strip & test hellow</h1></pre>", // 要分析的文本内容
        "analyzer": "test_html_strip_filter_analyzer"  // 使用的分析器名称
    }
    

    结果如下:

    在这里插入图片描述

    由结果可以看到,分词后的结果中没有h1标签,但是有pre标签。

    注意:这里的结果包含了&,这是因为我们的分析器中的分词器使用的是keyword,后面会详细介绍,这里重点关注保留了pre标签即可。

5.2.3 mapping 字符过滤器

  • 作用

    Mapping 字符过滤器用指定的替换字符串替换任何出现的指定字符串。

  • 示例

    将输入的文本内容中的“CN”替换成"中国"。

    // POST http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I come from CN",
        "tokenizer": "keyword", 
        "char_filter": [{
          
          
            "type":"mapping",
            "mappings":[
                "CN => 中国"
            ]
        }]
    }
    

    结果如下:

    在这里插入图片描述

    可以看到内容中的CN已经被替换成了中国

  • 使用自定义分析器

    在正式使用时,一般会结合自定义分析器来使用。

    创建带有自定义mapping字符过滤器的索引

    // PUT http://localhost:9200/mapping-filter-test
    // body内容
    {
          
          
    	"settings": {
          
          
    		"analysis": {
          
          
    			"analyzer": {
          
           
    				"test_mapping_filter_analyzer": {
          
            
    					"tokenizer": "keyword",
    					"char_filter": ["my_mapping_filter"] 
    				}
    			},
    			"char_filter": {
          
            
    				"my_mapping_filter": {
          
           
    					"type": "mapping", 
    					"mappings": [
                            "CN => 中国",
                            "HUBEI => 湖北",
                            "WUHAN => 武汉"
                        ] 
    				}
    			}
    		}
    	}
    }
    

    测试分词

    // GET http://localhost:9200/mapping-filter-test/_analyze
    // body内容
    {
          
          
        "text":"I come from WUHAN,HUBEI,CN",
        "analyzer": "test_mapping_filter_analyzer"
    }
    

    结果如下:

    在这里插入图片描述

    可以看到,输入的文本内容已经按照我们配置的映射进行了文本替换。

  • 使用配置文件

    当我们的mapping映射很多时,再使用这种脚本方式就不便于维护了。因此Elasticsearch支持通过配置mapping字符过滤器的属性mappings_path来读取指定目录下的文件,从而获取mapping映射。

    在Elasticsearch安装目录下的config子目录中,创建analysis文件夹,在其中放置my_mapping.txt文件,并在文件中维护对应的映射,如下图所示。

    在这里插入图片描述
    在这里插入图片描述

    注意:该txt文件必须是UTF-8编码,并且每个映射之间需要用换行符分隔

    创建索引进行测试

    // PUT http://localhost:9200/mapping-filter-test2
    // body内容
    {
          
          
    	"settings": {
          
          
    		"analysis": {
          
          
    			"analyzer": {
          
           
    				"test_mapping_filter_analyzer": {
          
            
    					"tokenizer": "keyword",
    					"char_filter": ["my_mapping_filter"] 
    				}
    			},
    			"char_filter": {
          
            
    				"my_mapping_filter": {
          
           
    					"type": "mapping", 
    					"mappings_path": "analysis/my_mapping.txt"
    				}
    			}
    		}
    	}
    }
    

    mappings_path需要指定读取的配置文件,该路径可以是基于config目录的相对路径或者绝对路径。

    // GET http://localhost:9200/mapping-filter-test2/_analyze
    // body内容
    {
          
          
        "text":"I come from WUHAN,HUBEI,CN",
        "analyzer": "test_mapping_filter_analyzer"
    }
    

    结果如下:

    在这里插入图片描述

5.2.4 pattern_replace 字符过滤器

  • 作用

    pattern_replace 字符过滤器用指定的替换替换与正则表达式匹配的任何字符。

  • 示例

    这里,我们直接使用自定义分析器来进行演示,替换文本内容中的电话号码为中文“这是一段电话号码”。

    // PUT http://localhost:9200/pattern-replace-filter-test
    // body内容
    {
          
          
    	"settings": {
          
          
    		"analysis": {
          
          
    			"analyzer": {
          
          
    				"test_pattern_replace_filter_analyzer": {
          
          
    					"tokenizer": "keyword",
    					"char_filter": ["my_pattern_replace_filter"]
    				}
    			},
    			"char_filter": {
          
          
    				"my_pattern_replace_filter": {
          
          
    					"type": "pattern_replace",
    					"pattern": "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$",
    					"replacement": "这是一段电话号码"
    				}
    			}
    		}
    	}
    }
    

    测试

    // GET http://localhost:9200/pattern-replace-filter-test/_analyze
    // body内容
    {
          
          
        "text":"13111111111",
        "analyzer": "test_pattern_replace_filter_analyzer"
    }
    

    结果如下:

    在这里插入图片描述

5.3 分词器

5.3.1 作用

分词器是对文本进行分析处理的一种手段,基本处理逻辑为按照预先制定的分词规则,把原始文本分割成若干更小粒度的词项,粒度大小取决于分词器规则

其工作时机在字符过滤器(若存在)之后。

系统内置了一些分词器,我们会详细介绍几种常用的分词器,其他分词器详见官方文档

5.3.2 standard 分词器

  • 作用

    该分词器会按照Unicode文本分割算法定义的词边界将文本划分为若干词项,并且分词器会去掉大部分的标点符号。

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I like steak !",
        "tokenizer": "standard"
    }
    

    结果如下:

    在这里插入图片描述

    可以看到,每个词分为了一个词项,且去掉了感叹号。

5.3.3 letter 分词器

  • 作用

    该分词器会在非字母文本处进行分割,即,当相邻词之间是非字母文本时,就会进行分割,并且也会去掉非字母的文本。

    注意:遇到中文时会按照是字母来处理,即,遇到中文时并不分割。

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I,like&steak和西红柿!and#you",
        "tokenizer": "letter"
    }
    

    结果如下:

    在这里插入图片描述

5.3.4 whitespace 分词器

  • 作用

    该分词器在遇到空格时进行分割。

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I like&steak!",
        "tokenizer": "whitespace"
    }
    

    结果如下:

    在这里插入图片描述

5.3.5 lowercase 分词器

  • 作用

    该分词器和letter分词器类似,会在非字母文本处进行分割,并且它会将分割出来的每个词项转成小写,此过滤器也会丢弃非字母文本的词项。

    相当于是letter分词器和lowercase词项过滤器的结合,但是性能更高。

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I,like&steak和西红柿!and#you",
        "tokenizer": "lowercase"
    }
    

    结果如下:

    在这里插入图片描述

5.3.6 classic 分词器

  • 作用

    该分词器基于语法规则对文本进行分词,对英语文档分词非常有用,在处理首字母缩写,公司名称,邮件地址和Internet主机名上效果非常好。

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"My mail is [email protected]",
        "tokenizer": "classic"
    }
    

    结果如下:

    在这里插入图片描述

5.3.7 keyword 分词器

  • 作用

    该分词器是一个“noop”标记器,它接收给定的任何文本,并输出作为单个词项的完全相同的文本。它可以结合词项过滤器来规范化输出,例如小写的电子邮件地址。

    即,将输入的文本当做一个整体

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I like steak !",
        "tokenizer": "keyword"
    }
    

    结果如下:

    在这里插入图片描述

5.3.8 pattern 分词器

  • 作用

    该使用正则表达式在匹配单词分隔符时将文本拆分为词项,或者将匹配的文本捕获为词项。

    默认模式是\W+,它在遇到非单词字符时拆分文本。

  • 示例

    匹配单词分隔符时将文本拆分成词项

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"test,pattern,tokenizer!!!",
        "tokenizer": {
          
          
            "type":"pattern",
            "pattern":","
        }
    }
    

    按照“,”分词

    结果如下:

    在这里插入图片描述

    将匹配的文本捕获为词项

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"\"test\"\"pattern,tokenizer!!!\"",
        "tokenizer": {
          
          
            "type":"pattern",
            "pattern":"\"((?:\\\\\"|[^\"]|\\\\\")+)\"",
            "group":1 
        }
    }
    

    捕获被双引号括起来的值

    这里的group,参考Java的Matcher对象的group() api

    结果如下:

    在这里插入图片描述

5.4 词项过滤器

5.4.1 作用

词项过滤器接受来自分词器的词项流,并可以修改词项(例如小写),删除词项(例如删除停止词)或添加词项(例如同义词)。

系统内置了一些词项过滤器器,我们会详细介绍几种常用的词项过滤器,其他词项过滤器详见官方文档

5.4.2 lowercase 词项过滤器

  • 作用

    该词项过滤器会将每个词项由大写改为小写,例如Man改为man,并且它支持希腊语、爱尔兰语和土耳其语的小写转换。

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I Like StEak !",
        "tokenizer": "keyword",
        "filter" : ["lowercase"]
    }
    

    结果如下:

    在这里插入图片描述

5.4.3 uppercase 词项过滤器

  • 作用

    该词项过滤器会将每个词项由小写改为大写,例如man改为MAN。

    注意:

    根据语言的不同,一个大写字符可以映射到多个小写字符。使用大写过滤器可能会导致丢失小写字符信息,因此更推荐使用lowercase词项过滤器

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I Like StEak !",
        "tokenizer": "keyword",
        "filter" : ["uppercase"]
    }
    

    结果如下:

    在这里插入图片描述

5.4.4 length 词项过滤器

  • 作用

    删除短于或长于指定字符长度的词项。例如,可以排除短于2个字符的词项和长于5个字符的词项。

    注意:

    该过滤器会删除不满足条件的整个词项。如果希望将词项缩短到特定长度,请使用 truncate 词项过滤器

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
    	"text": "I Like StEak !",
    	"tokenizer": "standard",
    	"filter": [{
          
          
    		"type": "length",
    		"min": 2,
    		"max": 4
    	}]
    }
    

    结果如下:

    在这里插入图片描述

5.4.5 truncate 词项过滤器

  • 作用

    该过滤器会截断超出指定字符限制的词项。此限制默认为10,但可以使用length参数自定义。

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
    	"text": "I Like StEak !",
    	"tokenizer": "keyword",
    	"filter": [{
          
          
    		"type": "truncate",
    		"length": 5
    	}]
    }
    

    结果如下:

    在这里插入图片描述

5.4.6 ASCII folding 词项过滤器

  • 作用

    该过滤器会将不在Basic Latin Unicode块中的字母、数字和符号字符(前127个ASCII字符)转换为它们的等价ASCII字符(如果存在)。例如,过滤器将à更改为a。

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
    	"text": "açaí à la carte",
    	"tokenizer": "keyword",
    	"filter": ["asciifolding"]
    }
    

    结果如下:

    在这里插入图片描述

5.4.7 stop 词项过滤器

  • 作用

    该过滤器会将词项中的停用词删除。

    如果没有自定义停用词,该过滤器会默认删除下列英文停用词:

    a, an, and, are, as, at, be, but, by, for, if, in, into, is, it, no, not, of, on, or, such, that, the, their, then, there, these, they, this, to, was, will, with

    除了英语,该过滤器还支持多种语言的预定义停止词列表,并且还可以将自己的停止词指定为数组或文件。

    详细配置请查看官方文档

  • 示例

    这里只做简单示例。

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
    	"text": "a quick fox jumps over the lazy dog",
    	"tokenizer": "standard",
    	"filter": ["stop"]
    }
    

    结果如下:

    在这里插入图片描述

5.5 常见的内置分析器

Elasticsearch提供了一些内置的分析器供用户使用。

接下来将会介绍一些常用的内置分析器,更多详细内置分析器见官方文档

5.5.1 standard 分析器

  • 作用

    该分析器是默认分析器,如果没有指定分析器,则默认使用它。它提供了基于Unicode文本分割算法的分词功能,它删除大多数标点符号,会将词项转换为小写,并支持删除停止词。适用于大部分语言,但是对中文支持的不理想,会逐字拆分,按词切分。

  • 组成

    • 字符过滤器

    • 分词器

      standard 分词器

    • 词项过滤器

      lowercase词项过滤器

      stop词项过滤器(默认不启用)

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I Like StEak !",
        "analyzer": "standard"
    }
    

    结果如下:

    在这里插入图片描述

5.5.2 simple 分析器

  • 作用

    该分析器在任何非字母字符处(如数字、空格、连字符和撇号)将文本分解为词项,并且会丢弃非字母字符、将大写改为小写。

  • 组成

    • 字符过滤器

    • 分词器

      lowercase 分词器

    • 词项过滤器

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I Like &StEak 和西红柿 2333 !",
        "analyzer": "simple"
    }
    

    结果如下:

    在这里插入图片描述

5.5.3 stop 分析器

  • 作用

    该分析器与simple分析器相同,但增加了删除停用词的支持。

  • 组成

    • 字符过滤器

    • 分词器

      lowercase 分词器

    • 词项过滤器

      stop 词项过滤器

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I Like &StEak the 和西红柿 2333 !",
        "analyzer": "stop"
    }
    

    结果如下:

    在这里插入图片描述

5.5.4 pattern 分析器

  • 作用

    该分析器使用正则表达式将文本拆分为各个词项,,并将词项由大写转换为小写。正则表达式应该匹配分隔符,而不是词项本身。正则表达式默认为\W+(所有非单词字符)。

    注意:这里使用的是Java正则表达式,如果正则表达式的性能较差,可能会导致stackoverflow异常。

  • 组成

    • 字符过滤器

    • 分词器

      pattern 分词器

    • 词项过滤器

      lowercase词项过滤器

      stop词项过滤器(默认不启用)

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I,Like&StEak,the,和西红柿,2333 !",
        "analyzer":"pattern"
    }
    

    结果如下:

    在这里插入图片描述

5.5.5 whitespace 分析器

  • 作用

    该分析器会在遇到空白字符时,将文本分解为词项。

  • 组成

    • 字符过滤器

    • 分词器

      whitespace分词器

    • 词项过滤器

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I Like &StEak the 和西红柿 2333 !",
        "analyzer":"whitespace"
    }
    

    结果如下:

    在这里插入图片描述

5.5.6 keyword 分析器

  • 作用

    该分析器是一个“noop”分析器,它将整个输入字符串作为单个词项返回。

  • 组成

    • 字符过滤器

    • 分词器

      keyword分词器

    • 词项过滤器

  • 示例

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"I Like &StEak the 和西红柿 2333 !",
        "analyzer":"keyword"
    }
    

    结果如下:

    在这里插入图片描述

5.6 自定义分析器

当Elasticsearch内置的分析器无法满足业务需求时,我们可以定义自定义分析器来实现业务需求。

自定义分析器也是由0或多个字符过滤器+一个分词器+0或多个词项过滤器组成的。

分析器常用配置参数如下

参数名 含义
type 分析器类型。接受内置分析器类型。对于自定义分析器,使用custom或省略此参数。
tokenizer 指定使用的分词器,可以是内置分词器或者自定义的分词器
char_filter 指定使用的字符过滤器,可以是内置的或者自定义的,这里接收的是一个数组。
filter 指定使用的词项过滤器,可以是内置的或者自定义的,这里接收的是一个数组。

下面我们将通过示例来讲解各个配置。

// PUT http://localhost:9200/custom-analyzer-test
// body内容
{
    
    
	"settings": {
    
    
		"analysis": {
    
    
			"analyzer": {
    
     // 当前索引中定义的分析器
				"my_analyzer": {
    
       // 自定义的分析器名称
                    "type":"custom", // 类型-自定义分析器
					"tokenizer": "my_tokenizer", // 自己定义的分词器
					"char_filter": ["&_to_and"],  // 自定义的分析器使用的字符过滤器集合
                    "filter":["my_stopwords","lowercase"] // 自定义的分析器使用的词项过滤器集合
				}
			},
			"char_filter": {
    
      // 当前索引中的字符过滤器集合
				"&_to_and": {
    
     // 自定义字符过滤器的名称
					"type": "mapping",  // 字符过滤器类型为mapping
                    "mappings":["& => and"]
				}
			},
            "tokenizer":{
    
     // 当前索引中定义的分词器
                "my_tokenizer":{
    
    
                    "type":"pattern",
                    "pattern":","
                }
            },
            "filter":{
    
      // 当前索引中定义的词项过滤器
                "my_stopwords":{
    
    
                    "type":"stop",
                    "stopwords": [ "the","a"]
                }
            }
		}
	}
}

测试自定义分析器

// GET http://localhost:9200/custom-analyzer-test/_analyze
// body内容
{
    
    
    "text":"I,Like & StEak,the,和西红柿,2333 !",
    "analyzer":"my_analyzer"
}

结果如下:

在这里插入图片描述

该自定义分析器的工作流程图如下图所示。

在这里插入图片描述

5.7 中文分析器

5.7.1 Elasticsearch对于中文的分析支持

在Elasticsearch提供的分析器中,对于中文的支持不够友好,它无法自动识别中文中的词组,例如:学习,学校等。

例如,使用standard分析器对“我要去学校学习”进行分析。

// GET http://localhost:9200/_analyze
// body内容
{
    
    
    "text":"我要去学校学习",
    "analyzer":"standard"
}

结果如下:

在这里插入图片描述

由上面的结果可以看出,使用Elasticsearch提供的分析器,无法满足我们对于中文分词的需求。

因此,我们常常使用IK分词器进行扩展。

5.7.2 IK分词器简介

IK分词器是免费开源的java分词器,是目前比较流行的中文分词器之一,它简单、稳定,但如果想要特别好的效果,需要自行维护词库,支持自定义词典。

5.7.3 IK分词器-安装

  • 下载地址

    https://github.com/medcl/elasticsearch-analysis-ik

    注意:要选择你的Elasticsearch对应版本的ik分词器

  • 部署

    将下载下来的IK分词器,解压在Elasticsearch安装目录的plugins子目录中。

在这里插入图片描述

解压完后,重启Elasticsearch即可。

注意:如果启动时报错如下:

java.lang.IllegalStateException: Could not load plugin descriptor for plugin directory [commons-codec-1.9.jar]
	at org.elasticsearch.plugins.PluginsService.readPluginBundle(PluginsService.java:403) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.findBundles(PluginsService.java:388) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.getPluginBundles(PluginsService.java:381) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.<init>(PluginsService.java:152) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.node.Node.<init>(Node.java:317) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.node.Node.<init>(Node.java:266) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap$5.<init>(Bootstrap.java:227) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:227) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:393) [elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:170) [elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:161) [elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) [elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:127) [elasticsearch-cli-7.8.0.jar:7.8.0]
	at org.elasticsearch.cli.Command.main(Command.java:90) [elasticsearch-cli-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:126) [elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92) [elasticsearch-7.8.0.jar:7.8.0]
Caused by: java.nio.file.NoSuchFileException: D:\Software\Work\elasticsearch-7.8.0\plugins\commons-codec-1.9.jar\plugin-descriptor.properties
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) ~[?:?]
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) ~[?:?]
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) ~[?:?]
	at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) ~[?:?]
	at java.nio.file.Files.newByteChannel(Files.java:361) ~[?:1.8.0_91]
	at java.nio.file.Files.newByteChannel(Files.java:407) ~[?:1.8.0_91]
	at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384) ~[?:1.8.0_91]
	at java.nio.file.Files.newInputStream(Files.java:152) ~[?:1.8.0_91]
	at org.elasticsearch.plugins.PluginInfo.readFromProperties(PluginInfo.java:156) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.readPluginBundle(PluginsService.java:400) ~[elasticsearch-7.8.0.jar:7.8.0]
	... 15 more
[2023-04-18T17:23:45,388][ERROR][o.e.b.ElasticsearchUncaughtExceptionHandler] [LAPTOP-AN1JMLBC] uncaught exception in thread [main]
org.elasticsearch.bootstrap.StartupException: java.lang.IllegalStateException: Could not load plugin descriptor for plugin directory [commons-codec-1.9.jar]
	at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:174) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.execute(Elasticsearch.java:161) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.cli.EnvironmentAwareCommand.execute(EnvironmentAwareCommand.java:86) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.cli.Command.mainWithoutErrorHandling(Command.java:127) ~[elasticsearch-cli-7.8.0.jar:7.8.0]
	at org.elasticsearch.cli.Command.main(Command.java:90) ~[elasticsearch-cli-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:126) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:92) ~[elasticsearch-7.8.0.jar:7.8.0]
Caused by: java.lang.IllegalStateException: Could not load plugin descriptor for plugin directory [commons-codec-1.9.jar]
	at org.elasticsearch.plugins.PluginsService.readPluginBundle(PluginsService.java:403) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.findBundles(PluginsService.java:388) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.getPluginBundles(PluginsService.java:381) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.<init>(PluginsService.java:152) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.node.Node.<init>(Node.java:317) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.node.Node.<init>(Node.java:266) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap$5.<init>(Bootstrap.java:227) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:227) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:393) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:170) ~[elasticsearch-7.8.0.jar:7.8.0]
	... 6 more
Caused by: java.nio.file.NoSuchFileException: D:\Software\Work\elasticsearch-7.8.0\plugins\commons-codec-1.9.jar\plugin-descriptor.properties
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) ~[?:?]
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) ~[?:?]
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) ~[?:?]
	at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) ~[?:?]
	at java.nio.file.Files.newByteChannel(Files.java:361) ~[?:1.8.0_91]
	at java.nio.file.Files.newByteChannel(Files.java:407) ~[?:1.8.0_91]
	at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384) ~[?:1.8.0_91]
	at java.nio.file.Files.newInputStream(Files.java:152) ~[?:1.8.0_91]
	at org.elasticsearch.plugins.PluginInfo.readFromProperties(PluginInfo.java:156) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.readPluginBundle(PluginsService.java:400) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.findBundles(PluginsService.java:388) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.getPluginBundles(PluginsService.java:381) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.plugins.PluginsService.<init>(PluginsService.java:152) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.node.Node.<init>(Node.java:317) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.node.Node.<init>(Node.java:266) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap$5.<init>(Bootstrap.java:227) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap.setup(Bootstrap.java:227) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Bootstrap.init(Bootstrap.java:393) ~[elasticsearch-7.8.0.jar:7.8.0]
	at org.elasticsearch.bootstrap.Elasticsearch.init(Elasticsearch.java:170) ~[elasticsearch-7.8.0.jar:7.8.0]
	... 6 more

可以在plugins建一层子目录ik,然后再把解压后的文件都放在ik子目录中

5.7.4 IK分词器配置讲解

在ik分词器的config目录中有一些ik分词器自带的配置。

在这里插入图片描述

各个配置的含义如下表所示

配置文件名 含义
main.dic 主词库
preposition.dic 语气词(也、而、但等)
stopword.dic 英文停用词
quantifier.dic 计量单位词
suffix.dic 后缀词(省、市、所等)
surname.dic 百家姓词库
extra_main.dic 扩展的主词库
extra_single_word.dic、extra_single_word_full.dic、extra_single_word_low_freq.dic 扩展的单字词库
extra_stopword.dic 扩展的停用词库
IKAnalyzer.cfg.xml IK分词器配置文件

5.7.5 IK分词器-内置分析器

在IK分词器中内置了两种分析器:ik_max_wordik_smart

  • ik_max_word

    将文本做最细粒度的拆分

  • ik_smart

    将文本做最粗粒度的拆分

  • 通过示例对比两种分析器

    ik_max_word

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"我要去学校学习",
        "analyzer":"ik_max_word"
    }
    

    结果如下:

    在这里插入图片描述

    ik_smart

    // GET http://localhost:9200/_analyze
    // body内容
    {
          
          
        "text":"我要去学校学习",
        "analyzer":"ik_smart"
    }
    

    结果如下:

在这里插入图片描述

5.7.6 IK分词器-扩展词组

当我们需要让某些字组成一个特定词组时,我们可以通过扩展词组的方式,进行自定义词组的扩展。

例如,“英雄联盟”,本身不是一个特定的词组,它会被分词器拆分。

下面我们来进行测试

// GET http://localhost:9200/_analyze
// body内容
{
    
    
    "text":"英雄联盟",
    "analyzer":"ik_smart"
}

结果如下:

在这里插入图片描述

可以看到,“英雄联盟”会被拆分成“英雄”和“联盟”两个词,这并不是我们想要的,我们想要“英雄联盟”被当做一个词项。

此时,我们可以通过在ik分词器的config目录中的IKAnalyzer.cfg.xml文件里进行配置。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict"></entry>
	 <!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords"></entry>
</properties>

我们做如下配置

<properties>
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict">mydic.dic</entry>
</properties>

在config目录中创建mydic.dic文件,把"英雄联盟"写入该文件,随后重启Elasticsearch即可。

下面我们再一次进行测试

// GET http://localhost:9200/_analyze
// body内容
{
    
    
    "text":"英雄联盟",
    "analyzer":"ik_smart"
}

结果如下:

在这里插入图片描述

可以看到,"英雄联盟"被当做了一个词项,不会被拆分了。

参考

【尚硅谷】ElasticSearch教程入门到精通(基于ELK技术栈elasticsearch 7.x+8.x新特性)

猜你喜欢

转载自blog.csdn.net/weixin_42584100/article/details/131904890
今日推荐