1. 前言
闲来无事,发现上一篇ES博客还是去年9月份写的中文ik分词器 pinyin 首字母 search_as_you_type 组合使用,该篇文章还挖了一个大坑没有填,快一年了,是时候填下坑了。
2. 期望的效果
针对股票查询这个特点场景,再结合一般使用者的搜索习惯,暂时确定如下7种期望效果。
2.1 中文名称
2.2 全称拼音首字母
2.3 中文简称
2.4 简称拼音首字母
2.5 拼音
2.6 中文+拼音
2.7 股票编号
3. 放弃search_as_you_type类型
上一篇博客Elasticsearch教程(34)中介绍了search_as_you_type类型,通过反复的实验验证,感觉search_as_you_type类型并不符合当前的场景,主要是不够灵活,对于简称“中行”、“建行”的支持不好。所以放弃使用它了,转而使用IK分词器 + pinyin分词器。
search_as_you_type类型对“建设银行”的处理如下:
字段 | 说明 |
---|---|
name | 按照 mapping 中的配置进行分析。 如果未配置分析器,则使用索引的默认分词器 |
name ._2gram | 用大小为 2 的 shingle token filter 分词器进行分词 |
name ._3gram | 用大小为 3 的 shingle token filter 分词器进行分词 |
name ._index_prefix | 用 edge ngram token filter 包装上面的 name ._3gram 的分词器 |
下面对name = "建设银行"这个短语分析
字段 | 分析后结果 |
---|---|
name | “建” 、“设”、“银”、“行” |
name ._2gram | “建 设”、“设 银”、“银 行” |
name ._3gram | “建 设 银”、“设 银 行” |
name ._index_prefix | “建”、"建 "、“建 设”、"建 设 "、“建 设 银” 等等 |
4. pinyin分词器的多音字的错误修改
网上关于pinyin分词器的安装和使用的博客特别多,这里我就不赘述了。但是我得说一个非常重要的问题,目前我写博客时最新的版本8.x还是有这样的问题。虽然GitHub上,已经有人提出了这个Issue,但是目前还没有修复,所以我们就自己动手手动改改吧。
就是多音字“银行”的“行”,pinyin分词器会把“yin hang”错误的转成“yin xing”,当你测试“中国银行”时它是对的,但是“建设银行”时就又错了。不信的话你自己试试看。
这个时候需要修改下图中的jar包解压出来
然后修改如下图中文件polyphone.txt
,注意千万不要一下子把"yin xing"替换成"yin hang"。
因为“隐形”、“银杏”这些词的拼音就是"yin xing"。这个你需要手动一个一个看好了改,我不确定这个问题是nlp-lang的问题还是pinyin分词器作者改的问题,我看nlp-lang1.7的源代码这个文件里“银行”确实是对的"yin hang"。
改好后,再重新打成nlp-lang-1.7.jar包,替换上图的那个nlp-lang-1.7.jar文件,然后重启ES就行啦。
5. 配置同义词文件和自定义字典
1 同义词
在ES的config目录下,新建analysis文件夹,再创建synonyms.txt,配置如下同义词
建设银行,建行
中国银行,中行
中信证券,中信,中证
中水渔业,中渔,中水
苏州科达,科达
2 自定义字典
在ik的config目录下创建ext.dic文本文件,添加一些自定义的扩展词
修改如下的配置文件,指定扩展字典的文件名
上面操作执行好了后,一定要重启ES,否则是不生效的。
6. 设计ES文档结构
ES的文档设计如下,这就是一个简单的以学习为目的实验性设计,所以设计很简陋。
- 别人博客会设计tokenizer为ik,filter为pinyin,但是我实验下来不是很理想(针对我的场景而言)
- 对于ik和pinyin我是分开的,中文+pinyin时用"name.pinyin",纯中文用"name.ik"或"name"
- 对于name主字段,“analyzer”:“standard”,原因我在上篇博客解释了,有使用的场景
- 对于name子字段,"keyword"特殊情况使用
- “firstLetter”,“shortName”,"shortNameFirstLetter"在代码中我没有用到,考虑后期优化先加着
- 对于无需聚合和排序的keyword,加下"doc_values":false,出于性能优化的目的
PUT pigg_stock_base
{
"settings":{
"analysis":{
"analyzer":{
"pinyin_analyzer":{
"tokenizer":"my_pinyin"
},
"ik_max_syno":{
"tokenizer":"ik_max_word",
"filter":"my_synonym"
},
"ik_smart_syno":{
"tokenizer":"ik_smart",
"filter":"my_synonym"
}
},
"tokenizer":{
"my_pinyin":{
"type":"pinyin",
"keep_first_letter":true,
"keep_separate_first_letter":true,
"keep_full_pinyin":true,
"keep_original":false,
"limit_first_letter_length":16,
"lowercase":true,
"remove_duplicated_term":true
}
},
"filter":{
"my_synonym":{
"type":"synonym",
"synonyms_path":"analysis/synonyms.txt"
}
}
}
},
"mappings":{
"properties":{
"name":{
"type":"text",
"analyzer":"standard",
"fields":{
"keyword":{
"type":"keyword",
"ignore_above":256,
"doc_values":false
},
"pinyin":{
"type":"text",
"analyzer":"pinyin_analyzer"
},
"ik":{
"type":"text",
"analyzer":"ik_max_syno",
"search_analyzer":"ik_smart_syno"
}
}
},
"firstLetter":{
"type":"keyword",
"doc_values":false
},
"shortName":{
"type":"keyword",
"doc_values":false
},
"shortNameFirstLetter":{
"type":"keyword",
"doc_values":false
},
"code":{
"type":"keyword"
}
}
}
}
插入测试数据
PUT pigg_stock_base/_doc/1
{
"name": "中国银行",
"firstLetter": "zgyh",
"shortName": ["中行"],
"shortNameFirstLetter": ["zh"],
"code": "601988"
}
PUT pigg_stock_base/_doc/2
{
"name": "中国石油",
"firstLetter": "zgsy",
"shortName": ["中石油"],
"shortNameFirstLetter": ["zsy"],
"code": "601857"
}
PUT pigg_stock_base/_doc/3
{
"name": "建设银行",
"firstLetter": "jsyh",
"shortName": ["建行"],
"shortNameFirstLetter": ["jh"],
"code": "601939"
}
PUT pigg_stock_base/_doc/4
{
"name": "中信证券",
"firstLetter": "zxzq",
"shortName": ["中信","中证"],
"shortNameFirstLetter": ["zx","zz"],
"code": "600030"
}
PUT pigg_stock_base/_doc/5
{
"name": "中信建设",
"firstLetter": "zxjs",
"shortName": [],
"shortNameFirstLetter": [],
"code": "601066"
}
PUT pigg_stock_base/_doc/6
{
"name": "中水渔业",
"firstLetter": "zsyy",
"shortName": ["中水"],
"shortNameFirstLetter": ["zs"],
"code": "000798"
}
7. 测试查询DSL语句
GET pigg_stock_base/_search
{
"query": {
"match": {
"name": {
"query": "中水渔业",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name.pinyin": {
"query": "中国银行",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name.ik": {
"query": "建设银行",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name": {
"query": "中国银",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name.pinyin": {
"query": "中国银",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name.pinyin": {
"query": "中国yh",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name.pinyin": {
"query": "中国yinh",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name.pinyin": {
"query": "中国yinhang",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name.pinyin": {
"query": "zgyh",
"operator": "and"
}
}
}
}
GET pigg_stock_base/_search
{
"query": {
"match": {
"name.pinyin": {
"query": "yh",
"operator": "and"
}
}
}
}
8. 代码
对于后端代码我就不放了,因为是学习ES的代码,有很多地方要优化,我拼接ES查询语句用的是之前我博客Elasticsearch教程(27) ES拼接查询条件的工具类提到的工具,这次又加了matchAnd,主要是为了提供如下的查询:
GET pigg_stock_base/_search
{
"query": {
"match": {
"name": {
"query": "中银行",
"operator": "and"
}
}
}
}
前端我用的Vue3 + Element-Plus,其实今年我的学习的目标是啥也可以不学,但是一定要把Vue3学会使用。
<script lang="ts">
export default {
name: "StockBase"
}
</script>
<template>
<div class="app-container">
<el-select
filterable
remote
reserve-keyword
placeholder="中文/首字母/拼音/代码"
:remote-method="handleSearch"
:loading="loading"
>
<el-option
v-for="item in options"
:key="item.code"
:label="item.name + '(' + item.code + ')'"
:value="item.id"
/>
</el-select>
</div>
</template>
<script setup lang="ts">
import {ref, reactive} from 'vue'
import {listStockBaseByKeyword} from '@/api/system/stockBase';
import {StockBase} from "@/types/api/system/stockBase";
const loading = ref(false);
const options = ref<StockBase[]>([])
//const dataState = reactive({});
function handleSearch(keyword: string) {
if (keyword) {
loading.value = true;
setTimeout(() => {
loading.value = false
listStockBaseByKeyword(keyword).then(({data}) => {
options.value = data
}
);
}, 200)
} else {
options.value = []
}
}
</script>
<style scoped>
</style>
9. 结语
这个功能主要是为了学习ES的分词器的使用,后端代码有很多优化的地方,比如考虑用ES的异步查询或折叠查询collapse,所以没有必要放后端代码。文章来源:https://www.toymoban.com/news/detail-406344.html
还有前端Vue3真的很重要,比Vue2添加了不少功能,光看文档不动手是没有用的,只有不断写页面才能真的学会前端。文章来源地址https://www.toymoban.com/news/detail-406344.html
到了这里,关于Elasticsearch教程(35) ik中文分词器+pinyin拼音分词器+同义词的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!