【ElasticSearch】ES自动补全查询与Java接口实现

news2024/9/29 5:33:27

文章目录

  • 1、安装拼音分词器
  • 2、自定义分词器
  • 3、completion suggester查询
  • 4、hotel索引库更新
  • 5、代码修改
  • 6、RestAPI实现自动补全
  • 7、需求:搜索框实现自动补全

自动补全就是当用户在搜索框输入字符时,我们应该提示出与该字符有关的搜索项。

在这里插入图片描述

1、安装拼音分词器

要实现根据字母做补全,就必须对文档按照拼音分词。GitHub上有相关插件,地址:https://github.com/medcl/elasticsearch-analysis-pinyin,下载和ES对应的版本。

安装步骤:

  • 解压

在这里插入图片描述

  • 上传到虚拟机中,elasticsearch的plugin目录

在这里插入图片描述

docker volume ls
docker inspect volumeXXX
  • 重启elasticsearch的容器
docker restar es
  • 测试

在这里插入图片描述

但上面的拼音分词器也有很明显的缺陷,那就是没有进行词条切割,且汉字没了。

2、自定义分词器

ES的分词器由三部分组成:

character filters:在tokenizer之前对文本进行处理。例如删除字符、替换字符

tokenizer:将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart

tokenizer filter:将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等

举个例子:

在这里插入图片描述
在创建索引库时, 可以通过settings来配置自定义的analyzer(分词器):

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": { // 自定义分词器
        "my_analyzer": {  // 分词器名称
          "tokenizer": "ik_max_word",
          "filter": "pinyin"
        }
      }
     }
  }
}
# 不需要替换或者删除,就不加character filter了

再改下定义tokenizer filter时的各种属性:

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": { // 自定义分词器
        "my_analyzer": {  // 分词器名称
          "tokenizer": "ik_max_word",
          "filter": "py"
        }
      },
      "filter": { // 自定义tokenizer filter
        "py": { // 过滤器名称
          "type": "pinyin", // 过滤器类型,这里是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
        }
      }
    }
  }
}

在这里插入图片描述

测试下效果:

在这里插入图片描述

插入两条name同音文档的后,搜索:

在这里插入图片描述

但此时搜一下中文看看:

在这里插入图片描述

很明显有问题,别人问狮子,你连同音词虱子都返回了。


拼音分词器适合在创建倒排索引时使用,但不能在搜索的时候使用。

创建倒排索引时,如下图:

在这里插入图片描述

但当使用拼音分词器来搜索时:

在这里插入图片描述

要解决这个问题,可以使用两个分词器:

"analyzer": "my_analyzer",   # 创建倒排索引时使用
"search_analyzer": "ik_smart" # 搜索时使用

PUT /test
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "ik_max_word", "filter": "py"
        }
      },
      "filter": {
        "py": { ... }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "my_analyzer",
        "search_analyzer": "ik_smart"  //!!!!
      }
    }
  }
}

在这里插入图片描述

重新测试下搜索中文的场景:

在这里插入图片描述
小结:

在这里插入图片描述

3、completion suggester查询

ES提供Completion Suggester查询 来实现自动补全功能,该查询会匹配以用户输入内容开头的词条并返回。此时,文档中字段的类型也有特殊要求:

  • 参与补全查询的字段必须是completion类型。
  • 字段的内容一般是用来补全的多个词条形成的数组
// 创建索引库
PUT test
{
  "mappings": {
    "properties": {
      "title":{
        "type": "completion"   //注意字段类型为completion
      }
    }
  }
}

// 示例数据
POST test/_doc
{
  "title": ["Sony", "WH-1000XM3"]
}
POST test/_doc
{
  "title": ["SK-II", "PITERA"]
}
POST test/_doc
{
  "title": ["Nintendo", "switch"]
}

completion suggester查询语法:

// 自动补全查询
GET /test/_search
{
  "suggest": { //查询类型,用suggest 
    "title_suggest": { //给你的suggest查询起个名
      "text": "s", // 用户输入的关键字
      "completion": {
        "field": "title", // 补全查询的字段
        "skip_duplicates": true, // 跳过重复的
        "size": 10 // 获取前10条结果
      }
    }
  }
}

运行DSL:

在这里插入图片描述

4、hotel索引库更新

看下之前的索引库的结构:

在这里插入图片描述
执行DSL来更新hotel索引库:

PUT /hotel
{
  "settings": {
    "analysis": {
      "analyzer": {
        "text_anlyzer": {  //定义第一个分词器
          "tokenizer": "ik_max_word",  //切割用ik_max
          "filter": "py"  //转换用拼音
        },
        "completion_analyzer": {   //定义第二个分词器,用于自动补全,不分词,直接转拼音
          "tokenizer": "keyword",  //分词用keyword,因为参与自动补全的是一个个词条,这些词条放在数组当中,本身就是个词条
          "filter": "py"
        }
      },
      "filter": {  //定义上面的拼音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",  //类型为completion
          "analyzer": "completion_analyzer"  //不分词,直接转拼音
      }
    }
  }
}

上面实现了:

  • 修改hotel索引库结构,设置自定义拼音分词器
  • 修改索引库的name、all字段,使用自定义分词器
  • 索引库添加一个新字段suggestion,类型为completion类型,使用自定义的分词器

5、代码修改

上面索引库更新后,上一节中的代码也要发生修改:

  • 给HotelDoc类添加suggestion字段,内容包含brand、business
  • 重新导入数据到hotel库
//HotelDoc类修改
@Data
@NoArgsConstructor
public class HotelDoc {
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String location;
    private String pic;
    //距离
    private Object distance;
    //是否充广告
    private Boolean isAD;
    //ES中的completion,后面存数组,这里可以对应成List
    private List<String> suggestion;

    public HotelDoc(Hotel hotel) {
        this.id = hotel.getId();
        this.name = hotel.getName();
        this.address = hotel.getAddress();
        this.price = hotel.getPrice();
        this.score = hotel.getScore();
        this.brand = hotel.getBrand();
        this.city = hotel.getCity();
        this.starName = hotel.getStarName();
        this.business = hotel.getBusiness();
        this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
        this.pic = hotel.getPic();
        this.suggestion = Arrays.asList(this.brand,this.business);

    }
}

注意上面的Array.asList方法,使suggestion字段内容包含brand、business

//运行从MySQL读数据,插入文档到ES的单元测试代码
@SpringBootTest
public class HotelDocumentTest {

    @Resource
    IHotelService iHotelService;

    private RestHighLevelClient client;

    @Test
    void testInit(){

        System.out.println(client);
    }

    @BeforeEach
    void setUp(){
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://10.4.130.220:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }


    @Test
    void testBulk() throws IOException {
        List<Hotel> hotels = iHotelService.list();
        BulkRequest request = new BulkRequest();
        for(Hotel hotel : hotels){
            HotelDoc hotelDoc = new HotelDoc(hotel);
            request.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc),XContentType.JSON)
            );
        }
        client.bulk(request,RequestOptions.DEFAULT);



    }


}

查看下文档数据:

在这里插入图片描述

在suggestion字段发现有数据的商业区有多个:

在这里插入图片描述

修改下HotelDoc的有参构造,加判断逻辑,business字段有斜杠/时,分开后再放入suggestion

@Data
@NoArgsConstructor
public class HotelDoc {
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String location;
    private String pic;
    //距离
    private Object distance;
    //是否充广告
    private Boolean isAD;
    //ES中的completion,后面存数组,这里可以对应成List
    private List<String> suggestion;

    public HotelDoc(Hotel hotel) {
        this.id = hotel.getId();
        this.name = hotel.getName();
        this.address = hotel.getAddress();
        this.price = hotel.getPrice();
        this.score = hotel.getScore();
        this.brand = hotel.getBrand();
        this.city = hotel.getCity();
        this.starName = hotel.getStarName();
        this.business = hotel.getBusiness();
        this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
        this.pic = hotel.getPic();
        if(this.business.contains("/")){
            //此时business有多个值,需要分开后放入suggestion
            String[] arr = this.business.split("/");
            //添加元素
            this.suggestion = new ArrayList<>();
            Collections.addAll(this.suggestion,arr);
            this.suggestion.add(this.brand);
        }else{
            this.suggestion = Arrays.asList(this.brand,this.business);
        }

    }
}


重新运行单元测试,插入文档数据,可以看到切割完成了:

在这里插入图片描述

执行自动补全查询的DSL,比如搜索h:

在这里插入图片描述

6、RestAPI实现自动补全

Java代码对比DSL来看,查询的实现是:

在这里插入图片描述

对响应结果的处理是:

在这里插入图片描述

在单元测试中使用一下先:

@Test
    void testSuggest() throws IOException {
        //1、准备Request
        SearchRequest request = new SearchRequest("hotel");
        //2、准备DSL
        request.source()
                .suggest(new SuggestBuilder().addSuggestion(
                        "mySuggestion",
                        SuggestBuilders.completionSuggestion("suggestion")
                                .prefix("h")  //搜索的关键字,这里用prefix,即前置,给方法起名很灵性
                                .skipDuplicates(true)
                                .size(10)
                ));
        //3、发起请求
        SearchResponse response = client.search(request,RequestOptions.DEFAULT);
        //4、解析结果
        Suggest suggest = response.getSuggest();
        //4.1 根据不全查询名称,获取查询结果
        CompletionSuggestion suggestion = suggest.getSuggestion("mySuggestion");
        //4.2 获取options
        List<CompletionSuggestion.Entry.Option> options = suggestion.getOptions();
        //4.3 遍历
        for (CompletionSuggestion.Entry.Option option : options) {
            String text = option.getText().toString();
            System.out.println(text);

        }
    }

运行得到以h开头的所有结果:

在这里插入图片描述

7、需求:搜索框实现自动补全

看下前端页面,每当在输入框键入时,前端会发送ajax请求:

在这里插入图片描述

接下来完善这个接口,实现搜索框的自动补全:

  • controller接口定义
import cn.itcast.hotel.domain.dto.RequestParams;
import cn.itcast.hotel.domain.vo.PageResult;
import cn.itcast.hotel.service.IHotelService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/hotel")
public class HotelSearchController {

    @Resource
    IHotelService hotelService;

    @GetMapping("/suggestion")
    public List<String> getSuggestions(@RequestParam("key") String prefix){
        return hotelService.getSuggestion(prefix);
    }



}
  • Service接口
public interface IHotelService extends IService<Hotel> {
 
    List<String> getSuggestion(String prefix);
}
  • 接口实现
@Override
public List<String> getSuggestion(String prefix) {
    try {
        SearchRequest request = new SearchRequest("hotel");
        request.source().suggest(new SuggestBuilder().addSuggestion(
                "mySuggestion",
                SuggestBuilders.completionSuggestion("suggestion")
                        .prefix(prefix)
                        .skipDuplicates(true)
                        .size(15)
        ));
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        Suggest suggest = response.getSuggest();
        CompletionSuggestion mySuggestion = suggest.getSuggestion("mySuggestion");
        List<CompletionSuggestion.Entry.Option> options = mySuggestion.getOptions();
        return options.stream()
                .map(t -> t.getText().toString())
                .collect(Collectors.toList());
    } catch (IOException e) {
        throw new RuntimeException();
    }
}



  • 关于client这个Bean,再补充下:
@SpringBootApplication
public class HotelDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(HotelDemoApplication.class, args);
    }

    @Bean
    public RestHighLevelClient client(){
        return new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://10.4.130.220:9200")
        ));
    }

}

重启服务,看下效果:

在这里插入图片描述

自动补全成功实现!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/737593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“小程序化”,一种创新的超级App开发模式

超级App是一种集成了多个功能和服务的移动应用程序&#xff0c;它在一个平台上提供了广泛的服务和体验。超级App通常具有大量的用户群体和高度活跃的用户社区&#xff0c;通过提供便利、多样化的功能&#xff0c;吸引用户在一个应用中完成多个任务和满足多个需求。 与传统的单…

postgresql 数据库 重建索引 所需时间测试

postgresql 数据库 重建索引 所需时间测试 文章目录 postgresql 数据库 重建索引 所需时间测试前言测试前准备重建索引前数据库状态测试计划重建索引命令测试开始1.先对表2进行测试2. 表3测试3. 表1测试 &#x1f308;后记 前言 众所周知&#xff0c;postgresql数据库使用久了…

【业务功能篇42】ThreadPoolTaskExecutor多线程处理耗时较高的数据接口

业务场景&#xff1a;当前业务模块中&#xff0c;有个查询产品直通率接口&#xff0c;随着数据量的递增&#xff0c;百万级数据&#xff0c;并且需要并表的情况下&#xff0c;那么返回数据就会开始变慢&#xff0c;而在数据层方面&#xff0c;已经比较难去做进一步的sql优化&am…

一致性哈希算法小结

在实际生产应用中&#xff0c;经常会设置多台服务器共同组成一个集成对外提供服务&#xff0c;为了确保合理的分配来自客户端的请求&#xff0c;我们会采取负载均衡的策略。例如采用「轮询」的方式让每个节点都能公平的接收到请求&#xff1b;采用「加权轮询」的方式让硬件配置…

MySQL-MySQL分组查询每组最新的一条数据

方法一&#xff1a; 1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ‘se_jck的博客-CSDN博客 这个错误是由于 MySQL 的新版本中默认开启了ONLY_FULL_GROUP_BY模式&#xff0c;即在 GROUP BY 语句中的 SELECT 列表中&am…

[MMDetection]测试模型

以下是基于MMdetection3.10版本 1、简单测试模型 测试模型一般使用tools中的test.py&#xff0c;一般使用方式 python tools/test.py config文件路径 权重文件路径 可以通过--show 来以gui展示检测结果 python tools/test.py config文件路径 权重文件路径 --show 可以通过--s…

【Linux】部署Prometheus + Grafana简介、监控及设置告警详细操作(多种方式安装,亲测无问题)

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 文章目录 一、环境准备二、部署 Prometheus&am…

优思学院|精益管理入门书籍有哪些推荐?

精益管理是一门易学难精的学问&#xff0c;如果对其基本原则了解不正确&#xff0c;可能会误入歧途&#xff0c;不但不能发挥精益工具的威力&#xff0c;甚至会令企业走向错误的方向&#xff0c;反带来更多的浪费和捐失。以下将介绍几本经典的书籍&#xff0c;可以让你有效地、…

python简单使用【mac-ide:pycharm】

小白实用快捷键记录 一、Mac下安装并配置python3开发环境二、python学习三、pycharm常用快捷键记录 一、Mac下安装并配置python3开发环境 点我查看python及pycharm下载安装、环境配置 二、python学习 不是很推荐&#xff0c;想系统学习的同学可以做个参考&#xff1a; Pytho…

排序之玩转qsort函数——【C语言】

说起排序&#xff0c;我们会想起许多算法&#xff0c;在之前的博客中我也写到过&#xff0c;比如&#xff1a;冒泡排序法、快速排序法、选择排序法等等。其实在C语言中一直有一个可以将数组中的内容进行排序的函数且功能完善内容齐全的库函数——qsort函数。今天就让我们来探索…

OpenPCDet系列 | 8.2 nuScenes数据集的eval流程

0. eval转换的目标 模型的训练和测试过程输出结果是不一样的&#xff0c;对于训练过程是为了构建损失函数来进行训练&#xff0c;而对于测试过程是为了对object进行预测生成预测内容。下面以VoxelNeX检测器的类代码可见&#xff0c;training和testing将会输出两个内容。 clas…

C++数据结构笔记(7)——队列的顺序结构实现

1.队列&#xff0c;和现实生活中的规则类似&#xff0c;先进先出 2.队尾只允许元素进入&#xff0c;队头只允许元素退出 3.用数组来实现队列的顺序存储&#xff0c;无论哪一段都可以作为队头或者队尾 SeqQueue.h头文件 #ifndef SEQQUEUE_H #define SEQQUEUE_H #include<…

仿大众点评项目 —— Day02【优惠券秒杀、分布式锁】

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

Java字符串类

string类的理解(以JDK8为例说明) 1.1的声明 public final class String implements java.io.Serializable&#xff0c; Comparable<String>&#xff0c; CharSequence final:String是不可被继承的 Serializable:可序列化的接口。凡是实现此接口的类的对象就可以通过…

建筑施工脚手架安全技术统一标准

为统一建筑施工脚手架设计、施工、使用及管理&#xff0c;做到技术先进、安全适用、经济合理&#xff0c;制定本标准。 本标准适用于房屋建筑工程和市政工程施工用脚手架的设计、施工、使用及管理。 建筑施工脚手架的设计、施工、使用及管理&#xff0c;除应符合本标准外&…

第一百零二天学习记录:数据结构与算法基础:初识数据结构与算法

管理系统模型&#xff08;仓库管理系统&#xff09;—顺序表 操作对象之间的关系&#xff1a;线性关系 数据结构&#xff1a;线性数据结构、线性表 &#xff08;例如&#xff1a;学生成绩管理系统、人事管理系统、仓库管理系统、通讯录等。&#xff09; 操作对象&#xff1a;若…

OWASP 定义的大模型应用最常见的10个关键安全问题

7月15日之前入驻华为云&#xff0c;可参与Check抽奖活动&#xff0c;抽奖活动在文末 1. 《OWASP 大模型应用最常见的10个关键安全问题》项目简介&#xff08;OWASP TOP10 LLMs Project&#xff09; *OWASP Top 10 for Large Language Model Applications OWASP 大模型应用程序…

vue3使用腾讯地图(‘关键词搜索、逆地址解析‘)

1.登录腾讯地图位置服务进入控制台 申请腾讯地图开发者进入控制台申请自己的key 腾讯位置服务 - 立足生态&#xff0c;连接未来 2.进入vue项目的public文件下的index.html 引入腾讯资源包&#xff0c;并把申请的key填入 <script src"https://map.qq.com/api/js?v2…

文心一言 VS 讯飞星火 VS chatgpt (57)-- 算法导论6.4 1题

文心一言 VS 讯飞星火 VS chatgpt &#xff08;57&#xff09;-- 算法导论6.4 1题 一、参照图 6-4 的方法&#xff0c;说明 HEAPSORT 在数组 A(5&#xff0c;13&#xff0c;2&#xff0c;25&#xff0c;7&#xff0c;17&#xff0c;20&#xff0c;8&#xff0c;4)上的操作过程…

怎么修复损坏的视频文件?视频文件修复办法分享!

随着科技的不断发展&#xff0c;我们的生活中已经离不开各种类型的视频文件。因为各式各样的原因&#xff0c;有时候我们的视频文件可能会损坏。 而损坏的视频文件通常是无法正常播放&#xff0c;这无疑会给我们的生活和工作造成极大的困扰。那么&#xff0c;怎么修复损坏的视…