Bootstrap

【Elasticsearch 】自定义分词器

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生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);
    }

    @Override
    public CharFilter create(Reader input) throws IOException {
        // 这里可以实现自定义的字符过滤逻辑,例如对特定字符的替换
        return new CustomCharFilter(input);
    }

    private static class CustomCharFilter extends CharFilter {
        public CustomCharFilter(Reader in) {
            super(in);
        }

        @Override
        public 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);
    }

    @Override
    public Tokenizer create(Reader input) {
        // 这里可以实现自定义的分词逻辑
        return new CustomTokenizer(input);
    }

    private static class CustomTokenizer extends Tokenizer {
        public CustomTokenizer(Reader input) {
            super(input);
        }

        @Override
        public 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);
    }

    @Override
    public TokenFilter create(TokenStream input) throws IOException {
        // 这里可以实现自定义的词项过滤逻辑
        return new CustomTokenFilter(input);
    }

    private static class CustomTokenFilter extends TokenFilter {
        public CustomTokenFilter(TokenStream input) {
            super(input);
        }

        @Override
        public 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 实战》
;