【Elasticsearch】学好Elasticsearch系列-分词器 2

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 【Elasticsearch】学好Elasticsearch系列-分词器

令牌过滤器(token filter)

在 Elasticsearch 中,Token Filter 负责处理 Analyzer 的 Tokenizer 输出的单词或者 tokens。这些处理操作包括:转换为小写、删除停用词、添加同义词等。

大小写和停用词

以下是一个例子,我们创建一个自定义分析器来演示如何使用 lowercasestop token filter

PUT /my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "standard",
          "filter": ["lowercase", "english_stop"]
        }
      },
      "filter": {
        "english_stop": {
          "type": "stop",
          //这里的 _english_ 是一个预设的停用词列表,
          //它包含了一些常用的英语停用词,如 "and", "is", "the" 等。
          "stopwords": "_english_"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}

在这个例子中,我们创建了一个名为 my_analyzer 的自定义分析器,它首先使用 standard 分词器将文本分割成 tokens,然后使用 lowercase 将所有 tokens 转换为小写形式,并使用 english_stop 过滤器移除英文停用词。

现在插入一条记录来测试:

PUT /my_index/_doc/1
{
  "text": "The Quick BROWN Fox Jumps Over THE Lazy Dog"
}

上述例子中的文本 "The Quick BROWN Fox Jumps Over THE Lazy Dog",运用我们自定义的 my_analyzer 分析器后,停用词(如 "The", "Over")将被剔除,并且所有的单词都会被转化为小写。所以这句话在进行索引和搜索时,实际上会被处理成:["quick", "brown", "fox", "jumps", "lazy", "dog"]。

同义词

synonym token filter 可以帮助我们处理同义词。它可以将某个词或短语映射到其它的同义词。

例如,假设你有一个电子商务网站,并且你想让搜索 "cellphone" 的用户也能看到所有包含 "mobile", "smartphone" 的商品。你可以使用 synonym token filter 来实现这一目标。

以下是一个使用 synonym token filter 的例子:

PUT /my_index
{
  "settings": {
    "analysis": {
      "filter": {
        "my_synonym_filter": {
          "type": "synonym",
          // "synonyms": ["赵,钱,孙,李=>吴", "周=>王"]
          //也可以像上面这种写法
          //当字段中出现"赵"、"钱"、"孙"或"李"时,会被替换成"吴"进行索引;
          //当字段中出现"周"时,会被替换成"王"进行索引。
          "synonyms": [
            "cellphone, mobile, smartphone"
          ]
        }
      },
      "analyzer": {
        "my_synonyms": {
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "my_synonym_filter"
          ]
        }
      }
    }
  }, 
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "my_synonyms"
      }
    }
  }
}

在这个设置中,我们创建了一个名为 my_synonym_filter 的同义词过滤器,并定义了 "cellphone", "mobile", "smartphone" 是互为同义词。然后我们在 my_synonyms 分析器中使用了该过滤器。

所以现在,无论你是输入 "cellphone", "mobile", 还是 "smartphone" 搜索,Elasticsearch 都会将其视为相同的查询。

我们可以使用synonyms_path 指定同义词规则路径,这个文件中列出了所有你定义的同义词,每行都是一组同义词,各词之间用逗号分隔。

使用 synonyms_path 参数的主要优点是,你可以在不重启 Elasticsearch 或重新索引数据的情况下,通过更新这个文件来动态地改变同义词规则。

假设你有一个名为 synonyms.txt 的文件,内容如下:

cellphone, mobile, smartphone tv, telly, television

然后你可以这样配置你的 index:

PUT /my_index
{
  "settings": {
    "index" : {
      "analysis" : {
        "analyzer" : {
          "my_analyzer" : {
            "tokenizer" : "standard",
            "filter" : ["lowercase", "my_synonym_filter"]
          }
        },
        "filter" : {
          "my_synonym_filter" : {
            "type" : "synonym",
            "synonyms_path" : "analysis/synonyms.txt"
          }
        }
      }
    } 
    }, 
    "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}

在这个设置中,我们创建了一个自定义分析器 my_analyzer ,并使用了一个自定义的同义词过滤器 my_synonym_filter。过滤器中的 synonyms_path 参数指向了存放同义词的 synonyms.txt 文件。

注意:synonyms_path 是相对于 config 目录的路径。例如,如果你的 config 目录在 /etc/elasticsearch/,那么 synonyms.txt 文件应该放在 /etc/elasticsearch/analysis/synonyms.txt

分词器(tokenizer)

在 Elasticsearch 中,分词器是用于将文本字段分解成独立的关键词(或称为 token)的组件。这是全文搜索中的一个重要过程。Elasticsearch 提供了多种内建的 tokenizer。

以下是一些常用的 tokenizer:

  1. Standard Tokenizer:它根据空白字符和大部分标点符号将文本划分为单词。这是默认的 tokenizer。
  2. Whitespace Tokenizer:仅根据空白字符(包括空格,tab,换行等)进行切分。
  3. Language Tokenizers:基于特定语言的规则来进行分词,如 englishfrench 等。
  4. Keyword Tokenizer:它接收任何文本并作为一个整体输出,没有进行任何分词。
  5. Pattern Tokenizer:使用正则表达式来进行分词,可以自定义规则。

你可以根据不同的数据和查询需求,选择适当的 tokenizer。另外,也可以通过定义 custom analyzer 来混合使用 tokenizer 和 filter(比如 lowercase filter,stop words filter 等)以达到更复杂的分词需求。

自定义分词器:custom analyzer

在 Elasticsearch 中,你可以创建自定义分词器(Custom Analyzer)。一个自定义分词器由一个 tokenizer 和零个或多个 token filters 组成。tokenizer 负责将输入文本划分为一系列 token,然后 token filters 对这些 token 进行处理,比如转换成小写、删除停用词等。

以下是一个自定义分析器的例子:

PUT /my_index
{
  "settings": {
    "analysis": {
      "filter": {
        "my_stopwords": {
          "type": "stop",
          "stopwords": ["the", "and"]
        }
      },
      "analyzer": {
        "my_custom_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "my_stopwords"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "text": {
        "type": "text",
        "analyzer": "my_custom_analyzer"
      }
    }
  }
}

在这个配置中,我们首先定义了一个名为 my_stopwords 的停用词过滤器,包含两个停用词 "the" 和 "and"。然后我们创建了一个名为 my_custom_analyzer 的自定义分析器,其中使用了 standard tokenizer 以及 lowercase filtermy_stopwords filter

因此,在为字段 text 索引文本时,Elasticsearch 会首先使用 standard tokenizer 将文本切分为 tokens,然后将这些 tokens 转换为小写,并移除其中的 "the" 和 "and"。对于搜索查询也同样适用此规则。

中文分词器:ik分词

elasticsearch 默认的内置分词器对中文的分词效果可能并不理想,因为它们主要是针对英文等拉丁语系的文本设计的。如果要在中文文本上获得更好的分词效果,我们可以考虑使用中文专用的分词器。

IK 分词器是一个开源的中文分词器插件,特别为 Elasticsearch 设计和优化。它在中文文本的分词处理上表现出色,能够根据中文语言习惯进行精细的分词。

安装和部署

ik文件描述

  • IKAnalyzer.cfg.xml:IK分词配置文件。
  • main.dic:主词库。
  • stopword.dic:英文停用词,不会建立在倒排索引中。
  • quantifier.dic:特殊词库:计量单位等。
  • suffix.dic:特殊词库:行政单位。
  • surname.dic:特殊词库:百家姓。
  • preposition:特殊词库:语气词。

ik提供的两种analyzer

  1. ik_max_word会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query。
  2. ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询。

ik自定义词库

要使用 IK 分词器的自定义词库,需要对 IK 插件的配置文件进行修改。步骤如下:

  1. 找到你 Elasticsearch 安装目录下的 plugins 文件夹,然后打开 ik 目录。
  2. ik 目录中,你会找到名为 config 的文件夹,这就是 IK 配置的位置。
  3. config 文件夹中新建一个文本文件,比如叫做 my_dict.dic,然后在这个文件中加入你自己的词汇,每行一个词。
  4. 接着打开 IKAnalyzer.cfg.xml 配置文件,在 标签内添加一行 my_dict.dic,告诉 IK 分词器你的自定义词库在哪里。
  5. 保存修改并重启 Elasticsearch,这时就可以使用自定义的词库了。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "[http://java.sun.com/dtd/properties.dtd](http://java.sun.com/dtd/properties.dtd)">
<properties>
  <comment>IK Analyzer 扩展配置</comment>
  <property name="ext_dict">my_dict.dic</property>
  <!--用户可以在这里配置自己的扩展字典 -->
  <property name="ext_stopwords"></property>
  <!--用户可以在这里配置自己的扩展停止词字典-->
</properties>

上述配置告诉 IK 分词器使用 my_dict.dic 作为扩展字典,但没有设置扩展的停用词字典。

注意这种方式只支持静态词库,一旦词库文件更改,则需要重启 Elasticsearch 才能加载新的词条。

热更新

要修改词库,必须重启ES才能生效,有时我们会频繁更新词库,比较麻烦,更致命的是,es肯定是分布式的,可

能有数百个节点,我们不能每次都一个一个节点上面去修改。基于这种场景,我们可以使用热更新功能。

实现热更新有2种办法:基于远程词库和基于数据库。

基于远程词库

IK 分词器支持从远程 URL 下载扩展字典,这就可以用来实现词库的热更新。

IKAnalyzer.cfg.xml 配置文件中,你可以设置 ext_dictext_stopwords 属性为一个指向你的在线词库文件的 URL:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "[http://java.sun.com/dtd/properties.dtd](http://java.sun.com/dtd/properties.dtd)">
<properties>
  <comment>IK Analyzer 扩展配置</comment>
  <property name="ext_dict">[http://myserver.com/my_dict.dic](http://myserver.com/my_dict.dic)</property>
  <!--用户可以在这里配置远程扩展字典 -->
  <property name="ext_stopwords">[http://myserver.com/my_stopwords.dic](http://myserver.com/my_stopwords.dic)</property>
  <!--用户可以在这里配置远程扩展停止词字典-->
</properties>

此设置告诉 IK 分词器从指定的 URL 下载词库。它会周期性地(默认每 60 秒)检查这些 URL,如果发现有更新,就重新下载并加载新的词库。

根据官方文档,该请求需要满足下列2点:

  1. 该 http 请求需要返回两个头部(header),一个是 Last-Modified,一个是 ETag,这两者都是字符串类型,只要有一个发生变化,该插件就会去抓取新的分词进而更新词库。
  2. 该 http 请求返回的内容格式是一行一个分词,换行符用 \n 即可。

满足上面两点要求就可以实现热更新分词了,不需要重启 ES 实例。

可以将需要自动更新的热词放在一个 UTF-8 编码的 .txt 文件里,放在 nginx 或其他简易 http server 下,当 .txt 文件修改时,http server 会在客户端请求该文件时自动返回相应的 Last-Modified 和 ETag。可以另外做一个工具来从业务系统提取相关词汇,并更新这个 .txt 文件。

基于远程词库这种方式比较简单上手,但是也存在一些缺点:

缺点:

  1. 词库的管理不方便,要操作直接操作磁盘文件,检索页很麻烦。
  2. 文件的读写没有专门的优化性能不好。
  3. 多一层接口调用和网络传输。

基于数据库

另外一种方式是基于数据库,这种方式使用比较多,但需要修改ik插件源码,有一定复杂度。

基本思路是将词库维护在数据库(MySQL,Oracle等),修改ik源码去数据库加载词库,然后将源码重新打包引入到我们的elasticsearch中。

大概操作步骤如下:

  1. 获取 IK 项目源码:首先从 GitHub 或其他地方获取 IK 分词器插件的源码。
  2. 设置数据库连接:在代码中设置好你的数据库连接参数,如数据库地址、用户名、密码等。
  3. 编写读取数据库词库的函数:编写一个可以从数据库读取词库数据并转换为 IK 分词器可以使用的格式(比如 ArrayList)的函数。
  4. 修改字典加载部分的代码:找到 IK 源码中负责加载扩展字典的部分,原本这部分代码是将文件内容加载到内存中,现在改为调用你刚才编写的函数,从数据库中加载词库数据。
  5. 添加定时任务:添加一个定时任务,每隔一段时间重新执行一次上述加载操作,以实现词库的热更新。
  6. 编译和安装:完成上述修改后,按照 IK 插件的构建说明,使用 Maven 或其他工具将其编译成插件,然后安装到 Elasticsearch 中。

本篇文章就到这里,感谢阅读,如果本篇博客有任何错误和建议,欢迎给我留言指正。

有收获?希望老铁来个三连,给更多的同学看到这篇文章,顺便激励下我,嘻嘻。

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
22天前
|
存储 自然语言处理 关系型数据库
ElasticSearch基础3——聚合、补全、集群。黑马旅游检索高亮+自定义分词器+自动补全+前后端消息同步
聚合、补全、RabbitMQ消息同步、集群、脑裂问题、集群分布式存储、黑马旅游实现过滤和搜索补全功能
ElasticSearch基础3——聚合、补全、集群。黑马旅游检索高亮+自定义分词器+自动补全+前后端消息同步
|
2月前
|
JSON 自然语言处理 Java
ElasticSearch 实现分词全文检索 - 搜素关键字自动补全(Completion Suggest)
ElasticSearch 实现分词全文检索 - 搜素关键字自动补全(Completion Suggest)
55 1
|
2月前
|
自然语言处理 Java 关系型数据库
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
ElasticSearch 实现分词全文检索 - 聚合查询 cardinality
30 1
|
2月前
|
自然语言处理 Java 索引
ElasticSearch 实现分词全文检索 - delete-by-query
ElasticSearch 实现分词全文检索 - delete-by-query
13 1
|
2月前
|
自然语言处理 索引
ElasticSearch 实现分词全文检索 - 测试数据准备
ElasticSearch 实现分词全文检索 - 测试数据准备
46 1
|
2月前
|
自然语言处理 索引
ElasticSearch 实现分词全文检索 - Restful基本操作
ElasticSearch 实现分词全文检索 - Restful基本操作
26 0
ElasticSearch 实现分词全文检索 - Restful基本操作
|
2月前
|
JSON 自然语言处理 数据库
Elasticsearch从入门到项目部署 安装 分词器 索引库操作
这篇文章详细介绍了Elasticsearch的基本概念、倒排索引原理、安装部署、IK分词器的使用,以及如何在Elasticsearch中进行索引库的CRUD操作,旨在帮助读者从入门到项目部署全面掌握Elasticsearch的使用。
|
2月前
|
自然语言处理 Java 关系型数据库
ElasticSearch 实现分词全文检索 - SpringBoot 完整实现 Demo 附源码【完结篇】
ElasticSearch 实现分词全文检索 - SpringBoot 完整实现 Demo 附源码【完结篇】
32 0
|
2月前
|
存储 自然语言处理 Java
ElasticSearch 实现分词全文检索 - 经纬度定位商家距离查询
ElasticSearch 实现分词全文检索 - 经纬度定位商家距离查询
15 0
|
2月前
|
自然语言处理 Java
ElasticSearch 实现分词全文检索 - 高亮查询
ElasticSearch 实现分词全文检索 - 高亮查询
51 0
下一篇
无影云桌面