ElasticSearch集成IK分词器
前述
为了能够更好地对中文进行搜索和查询,就需要在Elasticsearch中集成好的分词器插件, 而 IK
分词器就是用于对中文提供支持得插件。
集成IK分词器
下载
注意版本需要对应,目前
IK
分词器还没有8.3.3
版本,所以就先重新下一个8.2.3
版本的ES
安装
- 将下载的IK压缩包直接解压到
elasticsearch-8.2.3
的plugins
目录下,重启ES
使用 IK 分词器
IK 分词器提供了两个分词算法:
▶️ ik_smart
: 会做最粗粒度的拆分,适合 Phrase 查询
▶️ Ik_max_word
:会将文本做最细粒度的拆分,会穷尽各种可能的组合,适合 Term Query
- 为索引指定默认IK分词器
这样我们在索引中就不用创建每一个字段,可以通过动态字段映射,将String类型的字段映射为text类型,同时分词器指定为ik_max_word
PUT ik_index
{
"settings": {
"analysis": {
"analyzer": {
"default": {
"type": "ik_max_word"
}
}
}
}
}
- 使用
GET 索引名/_analyze
{
"analyzer": "ik_smart",
"text": ["分词词语测试"]
}
自定义分词效果
我们在使用
IK
分词器时会发现其实有时候分词的效果也并不是我们所期待的,有时一些特 殊得术语会被拆开,但实际上我们希望不要拆开。IK
插件给我们提供了自定义分词字典,我们可以添加自己想要保留得字了。
自定义分词
首先在
elasticsearch-analysis-ik-8.2.3
的config
目录下新建一个my_self.dic
文件,输入自己希望不想被拆开的术语,比如分词词语
接下来我们修改配置文件:
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">my_self.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
热更新 IK 分词使用方法
<!--用户可以在这里配置远程扩展字典 -->
<entry key="remote_ext_dict">location</entry>
<!--用户可以在这里配置远程扩展停止词字典-->
<entry key="remote_ext_stopwords">location</entry>
其中 location
是指一个 url,比如 http://yoursite.com/getCustomDict
,该请求只需满足以下两点即可完成分词热更新。
1、该 http 请求需要返回两个头部(header),一个是
Last-Modified
,一个是ETag
,这两者都是字符串类型,只要有一个发生变化,该插件就会去抓取新的分词进而更新词库。2、该 http 请求返回的内容格式是一行一个分词,换行符用
\n
即可。
满足上面两点要求就可以实现热更新分词了,不需要重启 ES 实例。
可以将需自动更新的热词放在一个 UTF-8
编码的 .txt
文件里,放在 nginx
或其他简易 http server
下,当 .txt
文件修改时,http server
会在客户端请求该文件时自动返回相应的 Last-Modified
和 ETag
。可以另外做一个工具来从业务系统提取相关词汇,并更新这个 .txt
文件。
常见问题
1.自定义词典为什么没有生效?
- 请确保扩展词典的文本格式为 UTF8 编码
2.分词测试失败,请在某个索引下调用analyze接口测试,而不是直接调用analyze接口 如:
curl -XGET "http://localhost:9200/your_index/_analyze" -H 'Content-Type: application/json' -d'
{
"text":"分词词语测试","tokenizer": "my_ik"
}'
JAVA-API
- 本实例在ElasticSearch-8.X的JAVA-API 基础上进行再扩展
/**
* @version 1.0.0
* @className: IndexTest
* @description: 创建索引并设置String列使用IK分词器
* @author: LiJunYi
* @create: 2022/8/8 10:03
*/
@SpringBootTest
@Slf4j
public class IndexTest
{
@Autowired
private ElasticsearchClient elasticsearchClient;
/**
* 创建索引并设置字段使用IK分词器
*
* @throws IOException ioexception
*/
@Test
void createIndexAndIk() throws IOException {
Map<String, Property> documentMap = new HashMap<>();
documentMap.put("userName",Property.of(p -> p
.text(TextProperty.of(textProperty ->
textProperty.index(true).analyzer("ik_max_word")))));
documentMap.put("age", Property.of(property ->
property.integer(IntegerNumberProperty.of(integerNumberProperty
-> integerNumberProperty.index(true))
)
)
);
CreateIndexResponse response = elasticsearchClient.indices().create(createIndexBuilder ->
createIndexBuilder.index("user").mappings(mappings ->
mappings.properties(documentMap))
.aliases("User",aliases ->
aliases.isWriteIndex(true))
);
//响应状态
boolean acknowledged = response.acknowledged();
boolean shardsAcknowledged = response.shardsAcknowledged();
String index = response.index();
log.info("创建索引状态:{}",acknowledged);
log.info("已确认的分片:{}",shardsAcknowledged);
log.info("索引名称:{}",index);
}
}