🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,
15年
工作经验,精通Java编程
,高并发设计
,Springboot和微服务
,熟悉Linux
,ESXI虚拟化
以及云原生Docker和K8s
,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea
【Elasticsearch 】自定义分词器
引言
在当今数字化信息爆炸的时代,文本数据的处理和分析变得至关重要。无论是搜索引擎、信息检索系统,还是智能客服、文本挖掘等应用场景,都离不开对文本的准确理解和分析。而在这一过程中,分词作为文本处理的基础环节,其效果直接影响到后续的数据分析和应用效果。
Elasticsearch 作为一款强大的分布式搜索引擎,提供了丰富的文本分析功能。然而,在实际的业务场景中,默认的分词器往往无法满足特定语言、业务需求或复杂文本处理要求。例如,在处理一些专业领域的文本时,如医学、法律等,需要根据专业术语和行业规范进行分词;对于一些具有特殊格式或结构的文本,也需要定制化的分词策略。
这就引出了我们今天要探讨的主题——Java Elasticsearch
自定义分词器。通过自定义分词器,开发者可以根据具体的场景,灵活配置字符过滤器、分词器、词项过滤器等组件,构建一套完全适合自身需求的文本分析流程。掌握这一技术,不仅能够提升文本处理的准确性和效率,还能为各种基于文本的应用带来更强大的功能和更好的用户体验。接下来,让我们一同深入学习如何在 Java 环境中利用 Elasticsearch 实现自定义分词器。
一、Elasticsearch 文本分析基础
1.1 文本分析流程概述
Elasticsearch 的文本分析是一个复杂但有序的过程,主要包括三个核心阶段:字符过滤(Character Filter)、分词(Tokenizer)和词项过滤(Token Filter)。
字符过滤阶段负责在文本被分词之前对原始文本进行预处理。它可以处理诸如 HTML 标签移除、特殊字符转换等任务。例如,如果我们的文本中包含 HTML 标签,字符过滤器可以将这些标签移除,只保留文本内容,这样可以避免在后续分词过程中标签对分词结果的干扰。
分词阶段是将文本按照一定的规则分割成一个个独立的词项(Token)。不同的分词器有不同的分词策略,比如标准分词器会按照单词边界进行分词,而中文分词器会根据中文的语义和语法规则进行分词。分词的准确性直接影响到后续的搜索和分析结果。
词项过滤阶段则是对已经分好的词项进行进一步的处理。比如,将词项转换为小写、移除停用词(如“的”“了”“是”等在文本中没有实际意义的词)、进行词干提取(将单词的不同形式转换为基本形式)等。通过词项过滤,可以进一步优化词项,提高搜索的精准度和召回率。
1.2 内置分词器介绍
Elasticsearch 提供了多种内置分词器,以满足不同的基本需求。
- 标准分词器(Standard Tokenizer):这是 Elasticsearch 的默认分词器。它按照 Unicode 文本分割算法将文本分割成词项,会去除标点符号等非字母数字字符。例如,对于文本“Hello, world! How are you?”,标准分词器会将其分词为“Hello”“world”“How”“are”“you”。它适用于处理大多数基于西方语言的文本。
- 简单分词器(Simple Tokenizer):简单分词器会在遇到非字母字符时进行分词。它会将所有词项转换为小写。例如,对于文本“Hello-World 123”,简单分词器会分词为“hello”“world”。
- 空格分词器(Whitespace Tokenizer):空格分词器非常简单,它仅仅根据空格来分割文本。对于文本“Hello world How are you”,它会分词为“Hello”“world”“How”“are”“you”。这种分词器适用于一些对格式有特定要求,且希望按照空格进行简单分割的场景。
- 中文分词器(如 IK 分词器):IK 分词器是 Elasticsearch 中常用的中文分词器,它有两种模式:细粒度模式和智能模式。细粒度模式会尽可能精确地将中文文本分词,例如“中华人民共和国”会被分词为“中华”“人民”“共和国”;智能模式则会根据语义进行更合理的分词,对于上述文本,智能模式可能会分词为“中华人民共和国”。
虽然这些内置分词器在很多情况下能够满足基本需求,但在面对复杂的业务场景时,往往需要自定义分词器来实现更精准的文本分析。
二、自定义分词器的组件
2.1 字符过滤器(Character Filter)
字符过滤器是文本分析流程的第一步,它用于对原始文本进行预处理。Elasticsearch 提供了一些内置的字符过滤器,同时也允许开发者自定义。
- HTML Strip Character Filter:这是一个非常实用的内置字符过滤器,它可以移除文本中的 HTML 标签。例如,对于文本“
Hello, world!
”,经过 HTML Strip Character Filter 处理后,会得到“Hello, world!”。在实际应用中,如果我们的文本数据来源包含 HTML 格式的内容,使用这个字符过滤器可以有效地清理文本,避免 HTML 标签对后续分词和分析的影响。 - Mapping Character Filter:Mapping Character Filter 可以根据预定义的映射规则对字符进行替换。例如,我们可以定义一个映射规则,将所有的“&”替换为“and”。通过配置映射文件,我们可以灵活地处理各种特殊字符的转换需求。
自定义字符过滤器需要继承 AbstractCharFilterFactory
类,并实现相应的方法。在 Java 中,我们可以这样实现一个简单的自定义字符过滤器:
import org.apache.lucene.analysis.CharFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.util.CharFilterFactory;
import org.elasticsearch.common.settings.Settings;import java.io.IOException;
import java.io.Reader;public class CustomCharFilterFactory extends CharFilterFactory {public CustomCharFilterFactory(Settings settings) {super(settings);}@Overridepublic CharFilter create(Reader input) throws IOException {// 这里可以实现自定义的字符过滤逻辑,例如对特定字符的替换return new CustomCharFilter(input);}private static class CustomCharFilter extends CharFilter {public CustomCharFilter(Reader in) {super(in);}@Overridepublic int read(char[] cbuf, int off, int len) throws IOException {// 实现具体的字符读取和过滤逻辑return super.read(cbuf, off, len);}}
}
2.2 分词器(Tokenizer)
分词器是自定义分词器的核心组件,它负责将文本分割成一个个词项。Elasticsearch 提供了多种内置分词器,同时也支持开发者自定义。
- Keyword Tokenizer:Keyword Tokenizer 不会对文本进行分词,而是将整个文本作为一个词项。例如,对于文本“Hello world”,Keyword Tokenizer 会将其作为一个整体的词项“Hello world”。这种分词器适用于一些需要保留原始文本格式的场景,比如处理 IP 地址、日期等。
- Pattern Tokenizer:Pattern Tokenizer 可以根据正则表达式对文本进行分词。通过定义正则表达式,我们可以灵活地控制分词的规则。例如,如果我们定义正则表达式为“\W+”(匹配非单词字符),那么对于文本“Hello, world! How are you?”,Pattern Tokenizer 会分词为“Hello”“world”“How”“are”“you”。
自定义分词器需要继承 TokenizerFactory
类,并实现相应的方法。以下是一个简单的自定义分词器示例:
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.util.TokenizerFactory;
import org.elasticsearch.common.settings.Settings;import java.io.Reader;public class CustomTokenizerFactory extends TokenizerFactory {public CustomTokenizerFactory(Settings settings) {super(settings);}@Overridepublic Tokenizer create(Reader input) {// 这里可以实现自定义的分词逻辑return new CustomTokenizer(input);}private static class CustomTokenizer extends Tokenizer {public CustomTokenizer(Reader input) {super(input);}@Overridepublic boolean incrementToken() throws IOException {// 实现具体的分词逻辑,填充词项return false;}}
}
2.3 词项过滤器(Token Filter)
词项过滤器用于对已经分好的词项进行进一步的处理和转换。
- Lowercase Token Filter:Lowercase Token Filter 会将所有词项转换为小写形式。例如,对于词项“Hello”,经过 Lowercase Token Filter 处理后会变为“hello”。在很多搜索场景中,将词项转换为小写可以提高搜索的准确性,避免因为大小写不一致而导致的搜索结果不完整。
- Stop Token Filter:Stop Token Filter 用于移除文本中的停用词。停用词是指在文本中没有实际意义的词,如“the”“and”“is”等。通过移除停用词,可以减少词项的数量,提高搜索效率和精准度。
自定义词项过滤器需要继承 TokenFilterFactory
类,并实现相应的方法。下面是一个简单的自定义词项过滤器示例:
import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.util.TokenFilterFactory;
import org.elasticsearch.common.settings.Settings;import java.io.IOException;public class CustomTokenFilterFactory extends TokenFilterFactory {public CustomTokenFilterFactory(Settings settings) {super(settings);}@Overridepublic TokenFilter create(TokenStream input) throws IOException {// 这里可以实现自定义的词项过滤逻辑return new CustomTokenFilter(input);}private static class CustomTokenFilter extends TokenFilter {public CustomTokenFilter(TokenStream input) {super(input);}@Overridepublic boolean incrementToken() throws IOException {// 实现具体的词项过滤逻辑return super.incrementToken();}}
}
三、构建自定义分词器
3.1 配置自定义分词器
在 Elasticsearch 中,配置自定义分词器需要在 elasticsearch.yml
文件或索引的映射文件中进行。以下是在索引映射文件中配置自定义分词器的示例:
{"settings": {"analysis": {"char_filter": {"custom_char_filter": {"type": "mapping","mappings": ["&=>and"]}},"tokenizer": {"custom_tokenizer": {"type": "pattern","pattern": "\\W+"}},"filter": {"custom_token_filter": {"type": "lowercase"}},"analyzer": {"custom_analyzer": {"type": "custom","char_filter": ["custom_char_filter"],"tokenizer": "custom_tokenizer","filter": ["custom_token_filter"]}}}}
}
在上述配置中,我们定义了一个自定义字符过滤器 custom_char_filter
,它将“&”替换为“and”;一个自定义分词器 custom_tokenizer
,它根据非单词字符进行分词;一个自定义词项过滤器 custom_token_filter
,它将词项转换为小写。最后,我们定义了一个自定义分析器 custom_analyzer
,它组合了上述定义的字符过滤器、分词器和词项过滤器。
3.2 在 Java 中使用自定义分词器
在 Java 中使用自定义分词器,我们需要借助 Elasticsearch 的 Java API。首先,我们需要添加相应的 Maven 依赖:
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.17.0</version>
</dependency>
<dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.17.0</version>
</dependency>
上述依赖中,elasticsearch-rest-high-level-client
提供了与 Elasticsearch 进行交互的高级 REST 客户端 API,elasticsearch
则是 Elasticsearch 的核心库。
接下来,我们可以在 Java 代码中使用自定义分词器进行文本分析:
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.analysis.AnalyzerProvider;
import org.elasticsearch.index.analysis.IndexAnalyzers;
import org.elasticsearch.rest.RestClient;import java.io.IOException;
import java.util.Map;public class CustomAnalyzerExample {public static void main(String[] args) throws IOException {RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));GetIndexRequest request = new GetIndexRequest("your_index_name");GetIndexResponse response = client.indices().get(request);Settings settings = response.getSettings();IndexAnalyzers indexAnalyzers = IndexAnalyzers.fromSettings(settings);AnalyzerProvider analyzerProvider = indexAnalyzers.getCustom("custom_analyzer");Analyzer analyzer = analyzerProvider.get();TokenStream tokenStream = analyzer.tokenStream("text", "Hello, &world! How are you?");CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);tokenStream.reset();while (tokenStream.incrementToken()) {System.out.println(charTermAttribute.toString());}tokenStream.end();tokenStream.close();client.close();}
}
在上述代码中,我们首先创建了一个 RestHighLevelClient
实例,用于与 Elasticsearch 进行通信。然后,我们通过 GetIndexRequest
获取索引的设置信息,从中提取出我们定义的自定义分析器 custom_analyzer
。接着,我们使用这个分析器对文本“Hello, &world! How are you?”进行分词,并输出分词结果。
四、实际案例:电商商品标题分词优化
4.1 业务场景分析
在电商系统中,商品标题的准确分词对于商品搜索和推荐至关重要。例如,当用户搜索“苹果手机”时,我们希望系统能够准确地将商品标题中包含“苹果手机”相关的商品检索出来。然而,默认的分词器可能无法很好地处理一些复杂的商品标题,比如包含品牌名、型号、功能等多种信息的标题。
4.2 自定义分词器设计
为了优化电商商品标题的分词效果,我们设计了一个自定义分词器。
- 字符过滤器:我们定义了一个字符过滤器,用于移除商品标题中的一些特殊字符,如括号、引号等,这些字符可能会干扰分词结果。
- 分词器:使用 Pattern Tokenizer 作为基础分词器,根据空格、下划线等字符进行分词。同时,我们针对电商领域的特点,对一些常见的品牌名、型号等进行特殊处理,确保这些关键信息不会被错误分词。
- 词项过滤器:添加了一个词项过滤器,用于移除一些在商品标题中常见但没有实际搜索意义的词,如“新款”“包邮”等。
4.3 代码实现与效果验证
在 Java 中实现上述自定义分词器,并将其应用到电商商品标题的索引和搜索中。通过实际的测试数据验证,使用自定义分词器后,商品搜索的准确率和召回率都有了显著提升,用户能够更准确地找到自己需要的商品。
五、总结
通过本文的学习,我们深入了解了 Java Elasticsearch 自定义分词器的相关知识。从 Elasticsearch 文本分析的基础原理,到自定义分词器的各个组件(字符过滤器、分词器、词项过滤器)的介绍,再到构建自定义分词器的具体步骤和实际案例应用,我们一步步掌握了如何根据特定的语言、业务需求或文本处理要求,打造适合自身场景的文本分析流程。
在实际的项目开发中,根据具体的业务场景灵活运用自定义分词器,可以极大地提升文本处理的准确性和效率,为用户提供更好的搜索和分析体验。希望本文的内容能够对广大开发者在 Elasticsearch 文本分析领域的工作有所帮助。
参考资料文献
- 《Elasticsearch 官方文档》
- 《Lucene 官方文档》
- 《Java Elasticsearch 实战》