微服务——ES实现自动补全

效果展示

在搜索框根据拼音首字母进行提示

拼音分词器

和IK中文分词器一样的用法,按照下面的顺序执行。

复制代码
# 进入容器内部
docker exec -it elasticsearch /bin/bash
 
# 在线下载并安装
./bin/elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v7.12.1/elasticsearch-analysis-pinyin-7.12.1.zip
 
#退出
exit
#重启容器
docker restart elasticsearch

重启完成之后进行拼音分词可以看见每个字都有,以及整个词语首字母组合成的一个。

自定义分词器

只用默认的功能还远远不够。

先用ik进行分词,再用拼音分词器分

复制代码
PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": { 
        "my_analyzer": { 
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      "filter": {
        "py": { 
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name":{
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}

在test这份索引库当中再次测试就可以看见既有中文也有拼音分词了。

java 复制代码
POST /test/_analyze
{
  "text":["北岭山脚鼠鼠"],
  "analyzer": "my_analyzer"
}

但是这里还会有问题,用中文搜索时会把同音字也一起搜索到

指定搜索时和创建时用不同的分词器

在上面的语句里面加上了一条

"search_analyzer": "ik_smart"

java 复制代码
POST /test/_doc/1
{
  "id": 1,
  "name": "狮子"
}
POST /test/_doc/2
{
  "id": 2,
  "name": "虱子"
}

GET /test/_search
{
  "query": {
    "match": {
      "name": "掉入狮子笼咋办"
    }
  }
}

结果如下

DSL实现自动补全查询

查询补全语法

数据准备

java 复制代码
// 自动补全的索引库
PUT test2
{
  "mappings": {
    "properties": {
      "title":{
        "type": "completion"
      }
    }
  }
}
// 示例数据
POST test2/_doc
{
  "title": ["Sony", "WH-1000XM3"]
}
POST test2/_doc
{
  "title": ["SK-II", "PITERA"]
}
POST test2/_doc
{
  "title": ["Nintendo", "switch"]
}

查询语句

java 复制代码
// 自动补全查询
GET /test2/_search
{
  "suggest": {
    "title_suggest": {
      "text": "s", // 关键字
      "completion": {
        "field": "title", // 补全字段
        "skip_duplicates": true, // 跳过重复的
        "size": 10 // 获取前10条结果
      }
    }
  }
}

酒店数据自动补全

修改酒店索引库数据结构

java 复制代码
DELETE /hotel
# 酒店数据索引库
PUT /hotel
{
  "settings": {
    "analysis": {
      "analyzer": {
        "text_anlyzer": {
          "tokenizer": "ik_max_word",
          "filter": "py"
        },
        "completion_analyzer": {
          "tokenizer": "keyword",
          "filter": "py"
        }
      },
      "filter": {
        "py": {
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "id":{
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "text_anlyzer",
        "search_analyzer": "ik_smart",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword",
        "index": false
      },
      "price":{
        "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
        "type": "keyword"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword",
        "copy_to": "all"
      },
      "location":{
        "type": "geo_point"
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "text_anlyzer",
        "search_analyzer": "ik_smart"
      },
      "suggestion":{
          "type": "completion",
          "analyzer": "completion_analyzer"
      }
    }
  }
}

先删除再重新创建一个

然后在HotelDoc这个实体类里面新增一个字段suggestion,这个字段是由现有的字段组成放进去。

复制代码
private List<String> suggestion;
复制代码
this.suggestion= Arrays.asList(this.brand,this.business);

然后重新执行之前的批量插入的语句

再次测试搜索可以看见搜索得到的结果里面多出了品牌和商圈信息。

但是这里business字段有可能是由多个的,要进行切割。

修改HotelDoc上面的构造方法的代码

java 复制代码
        if(this.business.contains("、")){
            //business有多个值,需要切割
            String[] arr = this.business.split("、");
            //添加元素
            this.suggestion=new ArrayList<>();
            this.suggestion.add(this.brand);
            Collections.addAll(this.suggestion,arr);
        }else {
            this.suggestion = Arrays.asList(this.brand, this.business);
        }

再次插入数据可以看见多个词条已经分开了。

进行搜索测试

搜索所有以h开头的词条

RestAPI实现自动补全

请求组装+响应解析

java 复制代码
    @Test
    void testSuggest() throws IOException {
        //1.准备request
        SearchRequest request = new SearchRequest("hotel");
        //2.准备DSl
        request.source().suggest(new SuggestBuilder().addSuggestion(
                "suggestion",
                SuggestBuilders.completionSuggestion("suggestion")
                        .prefix("h")
                        .skipDuplicates(true)
                        .size(10)
        ));
        //3.发起请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //4.解析结果
        Suggest suggest= response.getSuggest();
        //4.1根据补全查询名称,获取补全结果
        CompletionSuggestion suggestions = suggest.getSuggestion("suggestion");
        //4.2获取options
        List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
        //4.3遍历
        for (CompletionSuggestion.Entry.Option option : options) {
            String text = option.getText().toString();
            System.out.println(text);
        }
    }

实现搜索框自动补全

Controller中

java 复制代码
    @GetMapping("suggestion")
    public List<String>getSuggestion(@RequestParam("key")String prefix){
        return hotelService.getSuggestions(prefix);
    }

Service中

java 复制代码
    @Override
    public List<String> getSuggestions(String prefix) {
        try {
            //1.准备request
            SearchRequest request = new SearchRequest("hotel");
            //2.准备DSl
            request.source().suggest(new SuggestBuilder().addSuggestion(
                    "suggestion",
                    SuggestBuilders.completionSuggestion("suggestion")
                            .prefix(prefix)
                            .skipDuplicates(true)
                            .size(10)
            ));
            //3.发起请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //4.解析结果
            Suggest suggest= response.getSuggest();
            //4.1根据补全查询名称,获取补全结果
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestion");
            //4.2获取options
            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
            //4.3遍历
            List<String>list=new ArrayList<>(options.size());
            for (CompletionSuggestion.Entry.Option option : options) {
                String text = option.getText().toString();
                list.add(text);
            }
            return list;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

效果演示

成功根据提示进行查询

相关推荐
ai小鬼头5 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
掘金-我是哪吒7 小时前
分布式微服务系统架构第156集:JavaPlus技术文档平台日更-Java线程池使用指南
java·分布式·微服务·云原生·架构
国服第二切图仔7 小时前
文心开源大模型ERNIE-4.5-0.3B-Paddle私有化部署保姆级教程及技术架构探索
百度·架构·开源·文心大模型·paddle·gitcode
SelectDB8 小时前
SelectDB 在 AWS Graviton ARM 架构下相比 x86 实现 36% 性价比提升
大数据·架构·aws
weixin_437398219 小时前
转Go学习笔记(2)进阶
服务器·笔记·后端·学习·架构·golang
DavidSoCool10 小时前
RabbitMQ使用topic Exchange实现微服务分组订阅
分布式·微服务·rabbitmq
liulilittle10 小时前
SNIProxy 轻量级匿名CDN代理架构与实现
开发语言·网络·c++·网关·架构·cdn·通信
喷火龙8号10 小时前
深入理解MSC架构:现代前后端分离项目的最佳实践
后端·架构
Codebee11 小时前
“自举开发“范式:OneCode如何用低代码重构自身工具链
java·人工智能·架构
掘金-我是哪吒11 小时前
分布式微服务系统架构第158集:JavaPlus技术文档平台日更-JVM基础知识
jvm·分布式·微服务·架构·系统架构