07_es分布式搜索引擎3

news2024/12/28 8:22:32

一、数据聚合

1.聚合的分类

①聚合可以对文档数据的统计,分析,运算

②聚合的分类

  • 桶Bucket聚合:对文档按照字段分组
  • 度量Metric聚合:计算最大值,最小值,平均值
  • 管道pipeline聚合:以聚合的结果为基础聚合

③聚合的类型

  • keyword
  • 数值
  • 日期
  • 布尔

2.DSL实现Bucket聚合

案例:统计所有数据中酒店的品牌有几种,根据酒店品牌做聚合

得到的结果是从大到小

 

 ①结果排序

②限定聚合范围,加上query条件

// 只对200元以下的文档聚合

总结

①aggs表示聚合,与query同级。Query的作用是添加过滤条件。

②聚合三要素

 

③聚合的属性

size:聚合结果的数量

order:排序方式

field:聚合字段

3.DSL实现Metrics聚合

获取每个品牌(分类)的用户评分的min,max,avg,sum等值

 结果如下

4. RestAPI实现聚合

DSL构造

结果解析

代码

@Test
void test() throws IOException{
   SearchRequest request = new SearchRequest("hotel");
   request.source().size(0);//不显示具体的文档内容
   request.source().aggregation(AggregationBuilders
         .terms("brand_agg")
         .field("brand")
         .size(20));
   SearchResponse response = client.search(request, RequestOptions.DEFAULT);
   // 2.解析聚合结果
   Aggregations aggregations = response.getAggregations();
   // 根据名称得到Buckets
   Terms brandTerms = aggregations.get("brand_agg");
   // 获取桶
   List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
   // 遍历
   for (Terms.Bucket bucket : buckets) {
       String brandName = bucket.getKeyAsString();
      System.out.println(brandName);
   }

}

5.案例:在IUserService中定义方法,实现对品牌、城市、星级的聚合,展示给前端页面关键字

/**
 * 前端展示的聚合字段
 */
@Override
public Map<String, List<String>> filters() throws IOException {
   // 创建map对象
   Map<String, List<String>> map = new HashMap();
   // 创建request请求
   SearchRequest request = new SearchRequest("hotel");
   // 组织DSL size,agg,
   request.source().size(0);
   // 聚合
   buildAggregation(request);
   // 发送请求得到响应
   SearchResponse response = client.search(request, RequestOptions.DEFAULT);
   // 解析
   Aggregations aggregations = response.getAggregations();
   // 解析1:city
   List<String> cityList = getAggList(aggregations,"cityAgg");
   List<String> brandList = getAggList(aggregations,"brandAgg");
   List<String> starList = getAggList(aggregations,"starAgg");
   map.put("城市",cityList);
   map.put("品牌",brandList);
   map.put("星级",starList);
   return map;
}

/**
 * 代码抽取:获取数据
 * @param aggregations
 * @param aggName
 * @return
 */
private List<String> getAggList(Aggregations aggregations,String aggName) {
   Terms cityTerms = aggregations.get(aggName);
   final List<? extends Terms.Bucket> buckets = cityTerms.getBuckets();
   List<String> list = new ArrayList();
   for (Terms.Bucket bucket : buckets) {
      list.add(bucket.getKeyAsString());
   }
   return list;
}

/**
 * 代码抽取:聚合
 *
 * @param request
 */
private void buildAggregation(SearchRequest request) {
   request.source().aggregation(AggregationBuilders
         .terms("brandAgg")
         .field("brand")
         .size(100)
   );
   request.source().aggregation(AggregationBuilders
         .terms("cityAgg")
         .field("city")
         .size(100)
   );
   request.source().aggregation(AggregationBuilders
         .terms("starAgg")
         .field("starName")
         .size(100)
   );
}

二、自动补全

1.安装拼音分词器

要实现根据字母做补全,就必须对文档按照拼音分词。

elasticsearch的拼音分词插件GitHub - medcl/elasticsearch-analysis-pinyin: This Pinyin Analysis plugin is used to do conversion between Chinese characters and Pinyin.

安装方式分三步:

①  解压

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

③  重启elasticsearch

④  测试

POST /_analyze
{
  "text": "如家酒店",
  "analyzer": "pinyin"
}

 

2.自定义分词器

以上的不够智能,需要改进。

拼音都是单个字或者全部一句话的首字母

①es分词器analyzer的组成:

  • character filters:删除字符、替换字符
  • tokenizer:将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart
  • tokenizer filter:将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等

 

②自定义分词器

  • 通过settings来配置自定义的analyzer(分词器)。创建索引库test,name字段使用自定义分词器
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"
      }
    }
  }
}

测试

POST /test/_analyze
{
  "text": "如家酒店",
  "analyzer": "my_analyzer"
}

结果中英文都有

所以创建倒排索引(添加)的时候可以使用拼音分词,搜索的时候不能使用,因为容易搜到同音字。两个要分别创建 

 ③字段在创建倒排索引时使用自定义拼音分词器(多个搜索的可能),字段在搜索的时候使用ik_smart分词器(中文只能匹配一种,拼音缩写是可能是多种)

总结

①如何自定义分词器?

创建索引库时,在settings中配置,可以包含三部分

character filter

tokenizer

filter

②拼音分词器注意事项?

为了避免搜索到同音字,搜索时不要使用拼音分词器

3. completion suggester查询实现自动补全

①es提供completion suggester查询实现自动补全功能。这个查询会匹配用户输入的内容开头词条并返回。

参与补全查询的字段必须是completion类型。

字段的内容一般是用来补全的多个词条形成的数组

 查询

 

4.实现hotel索引库的自动补全、拼音搜索功能

实现思路如下:

  • 修改hotel索引库结构,设置自定义拼音分词器
  • 修改索引库的name、all字段,使用自定义分词器
  • 索引库添加一个新字段suggestion,类型为completion类型,使用自定义的分词器
  • 给HotelDoc类添加suggestion字段,内容包含brand、business
  • 重新导入数据到hotel库

注意:name、all是可分词的,自动补全的brand、business是不可分词的,要使用不同的分词器组合

①修改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字段,内容包含brand、business

@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;
    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("/")){
            // 如果多个商圈
            this.suggestion = new ArrayList<>();
            suggestion.add(this.brand);
            String[] arr = this.business.split("/");
            Collections.addAll(this.suggestion,arr);
        }else {
            this.suggestion = Arrays.asList(this.brand,this.business);
        }
    }
}

⑤具体实现

 

/**
 * 自动补全
 *
 * @param key
 * @return
 */
@Override
public List<String> getSuggestion(String key) {
   List<String> suggestionList = new ArrayList<>();
   // 创建请求
   SearchRequest request = new SearchRequest("hotel");
   // 编写DSL
   request.source().suggest(new SuggestBuilder().addSuggestion(
         "hotelSuggestion",
         SuggestBuilders.completionSuggestion("suggestion")
               .prefix(key)
               .skipDuplicates(true)
               .size(10)
   ));
   // 发送请求
   try {
      SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

      // 解析
      Suggest suggest = response.getSuggest();
      // 根据名称获取
      CompletionSuggestion suggestion = suggest.getSuggestion("hotelSuggestion");
      // 遍历
      for (CompletionSuggestion.Entry.Option option : suggestion.getOptions()) {
         String text = option.getText().string();
         suggestionList.add(text);

      }


   } catch (IOException e) {
      e.printStackTrace();
   }

   return suggestionList;
}

三、数据同步

数据同步问题分析

es的数据来自mysql,当mysql发生改变时,es的数据要同时改变。E  s与mysql之间要数据同步

异步通知

优点:低耦合,实现难度一般。

缺点:依赖mq的可靠性

 

案例:利用MQ实现mysql与elasticsearch数据同步

利用课前资料提供的hotel-admin项目作为酒店管理的微服务。当酒店数据发生增、删、改时,要求对elasticsearch中数据也要完成相同操作。

步骤:

  • 导入课前资料提供的hotel-admin项目,启动并测试酒店数据的CRUD
  • 声明exchange、queue、RoutingKey
  • 在hotel-admin中的增、删、改业务中完成消息发送
  • 在hotel-demo中完成消息监听,并更新elasticsearch中数据
  • 启动并测试数据同步功能

1. 声明exchange、queue、RoutingKey

当酒店发生增改,删除时发消息

消费者:hotel-demo声明exchange

①导入坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

②配置文件

③声明队列交换机

 

MqConstants常量 

public class MqConstants {
   /**
    * 交换机
    */
   public final static String HOTEL_EXCHANGE = "hotel.topic";
   /**
    * 监听队列
    * 新增,修改
    */
   public final static String HOTEL_INSERT_QUEUE = "hotel.insert.queue";
   /**
    * 监听队列
    * 删除
    */
   public final static String HOTEL_DELETE_QUEUE = "hotel.delete.queue";
   /**
    * 新增或修改的RoutingKey
    */
   public final static String HOTEL_INSERT_KEY = "hotel.insert";
   /**
    * 删除RoutingKey
    */
   public final static String HOTEL_DELETE_KEY = "hotel.delete";
}

基于bean声明交换机对象

@Configuration
public class MqConfiguration {
   /**
    * 交换机定义
    *
    * @return
    */
   @Bean
   public TopicExchange topicExchange() {
      return new TopicExchange(MqConstants.HOTEL_EXCHANGE, true, false);
   }

   /**
    * 队列定义:insert和update
    */
   @Bean
   public Queue insertQueue() {
      return new Queue(MqConstants.HOTEL_INSERT_QUEUE, true);
   }

   /**
    * 队列定义:delete
    */
   @Bean
   public Queue deleteQueue() {
      return new Queue(MqConstants.HOTEL_DELETE_QUEUE, true);
   }

   /**
    * 绑定关系 insertQueue
    * bind队列--to交换机--with
    */
   @Bean
   public Binding insertQueueBinding() {
      return BindingBuilder.bind(insertQueue()).to(topicExchange()).with(MqConstants.HOTEL_INSERT_KEY);
   }

   /**
    * 绑定关系 deleteQueue
    * bind队列--to交换机--with
    */
   @Bean
   public Binding deleteQueueBinding() {
      return BindingBuilder.bind(deleteQueue()).to(topicExchange()).with(MqConstants.HOTEL_DELETE_KEY);
   }

}

2.在hotel-admin中的增、删、改业务中完成消息发送

①复制MqConstants常量到hotel-admin

②同样导入队列amqp的maven坐标

③同样配置amqp地址

④消息发送的代码在controller

新增/修改

@Autowired
private RabbitTemplate rabbitTemplate;
用于构建发送和接收消息的客户端应用程序
@PostMapping
public void saveHotel(@RequestBody Hotel hotel){
    hotelService.save(hotel);
    rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE,MqConstants.HOTEL_INSERT_KEY,hotel.getId());
}

发送消息rabbitTemplate.convertAndSend(交换机,新增RoutingKey,消息内容(酒店id))

删除

@DeleteMapping("/{id}")
public void deleteById(@PathVariable("id") Long id) {
    hotelService.removeById(id);
    rabbitTemplate.convertAndSend(MqConstants.HOTEL_EXCHANGE,MqConstants.HOTEL_DELETE_KEY,id);

}

3.在hotel-demo中完成消息监听,并更新elasticsearch中数据

①创建监听类HotelListener,定义消费者,接收消息

@Component
public class HotelListener {
   @Autowired
   private IHotelService hotelService;
   /**
    * 监听新增或修改的业务
    * @param id
    */
   @RabbitListener(queues = MqConstants.HOTEL_INSERT_QUEUE)
   public void listenHotelInsertOrUpdate(Long id){
      hotelService.insertById(id);
   }

   /**
    * 监听删除业务
    * @param id
    */
   @RabbitListener(queues = MqConstants.HOTEL_DELETE_QUEUE)
   public void listenHotelDelete(Long id) {
      // 删除索引库
      hotelService.deleteById(id);
   }

}

新增或修改hotelService.insertById(id);

@Override
public void insertById(Long id) {
   try {
      // 0.根据id查询数据
      Hotel hotel = getById(id);
      HotelDoc hotelDoc = new HotelDoc(hotel);
      // 1.准备Request
      IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString());
      // 2.准备DSL
      request.source(JSON.toJSONString(hotelDoc), XContentType.JSON);
      // 3.发送请求
      restHighLevelClient.index(request,RequestOptions.DEFAULT);
   } catch (IOException e) {
      throw new RuntimeException(e);
   }
}

删除索引hotelService.deleteById(id);

@Override
public void deleteById(Long id) {
   try {
      // 1.准备request
      DeleteRequest request  = new DeleteRequest("hotel",id.toString());
      // 2.发送请求
      restHighLevelClient.delete(request,RequestOptions.DEFAULT);
   } catch (IOException e) {
      throw new RuntimeException(e);
   }
}

4.启动并测试数据同步功能

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

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

相关文章

Banana Pi BPI-W3 RK3588开发平台批量产测软件,全面批量测试

优秀的产品都要进行严苛的产品测试才能够经得起市场的检验由ArmSoM团队研发的产测软件用于在量产的过程中快速地甄别产品功能和器件的好坏&#xff0c;即重点 FCT&#xff08;Functional Test&#xff09;测试&#xff0c;进而提高生产效率和检测的准确性。ArmSoM团队的专业产测…

第二证券:需求回暖叠加价值提升 机构加码PCB板块

受华为手机、小米手机近期广受欢迎等利好消息提振&#xff0c;叠加AI板块的爆发&#xff0c;作为中心零部件的 PCB&#xff08;印制电路板&#xff09;板块后市可期&#xff0c;部分安排近期加码布局。 量价齐升 后市可期 安排布局PCB板块的理由主要有两个&#xff0c;一是需…

chrome driver下载、selenium安装及报错解决

目录 一、Chrome驱动下载 1.查看Chrome版本 2.下载驱动 3.驱动的路径 无法运行驱动 二、selenium的安装与使用 1.安装selenium 2.使用selenium 参考 一、Chrome驱动下载 1.查看Chrome版本 打开Chrome浏览器&#xff0c;点击右上角的三个点&#xff0c;再点击设置。 …

老胡的周刊(第114期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 pyvideotrans[2] 将视频从一种语言翻译为另一…

什么是大模型?一文读懂大模型的基本概念

大模型是指具有大规模参数和复杂计算结构的机器学习模型。本文从大模型的基本概念出发&#xff0c;对大模型领域容易混淆的相关概念进行区分&#xff0c;并就大模型的发展历程、特点和分类、泛化与微调进行了详细解读&#xff0c;供大家在了解大模型基本知识的过程中起到一定参…

mac电脑邮件附件清理工具CleanMyMacX2024

邮件附件清理功能可以保证在收件箱中原始附件的安全性的基础上&#xff0c;清理邮件下载和附件的本地副本&#xff0c;回收大量的磁盘空间。 在默认情况下&#xff0c;当您打开或者查看新的邮件附件时&#xff0c;应用程序将将其副本存储到磁盘上直到您删除相关的电子邮件。在…

智行破晓,驭未来航程!——经纬恒润智能驾驶数据闭环云平台OrienLink重磅来袭

2023是被AI技术标记的⼀年。年初&#xff0c;OpenAI的GPT崭露头角&#xff1b;6月&#xff0c;Tesla在CVPR2023上对World Model进行深度解读&#xff1b;8月&#xff0c;SIGGRAPH见证GH200、L40S显卡和ChatUSD的登场&#xff0c;FSD V12彰显端到端智能驾驶的实力&#xff1b;9月…

希亦内衣洗衣机和小米哪个品牌好?内衣洗衣机横评对比

内衣洗衣机作为一种小型家电&#xff0c;受到越来越多人的欢迎。内衣洗衣机虽然体积小&#xff0c;但功能并不简单。我们可以选择具备多种洗涤模式、容量适中、节能环保的洗衣机&#xff0c;以满足我们的不同需求。那么面对希亦以及小米这两个热门的洗衣机品牌&#xff0c;我们…

MyBatis 分页插件 PageHelper 6.0.0 发布

6.0.0 - 2023-11-05 基于jdk8适配&#xff0c;6.0开始不支持jdk6和7&#xff0c;如果有需要可以使用5.x版本增加异步count支持&#xff0c;全局配置asyncCount&#xff0c;默认false&#xff0c;单次设置&#xff1a;PageHelper.startPage(1, 10).enableAsyncCount(); 异步使用…

Tuxera NTFS2023永久免费版本下载

若我们想要将一款格式为NTFS的磁盘转换成FAT32格式&#xff0c;如何通过NTFS for Mac进行转换呢&#xff1f;使用过这款软件的用户都之后&#xff0c;这款产品包含一个Disk Manager组件&#xff0c;通过这个组件我们可以对磁盘进行管理操作&#xff0c;所以想要将磁盘格式进行转…

SM5102 3.7V 锂电池转干电池充放管理芯片

SM5102 3.7V 锂电池转干电池充放管理芯片 简介 &#xff1a; SM5102 是一款锂电池充放电管理专用芯片。充电工作时, 可以为 3.7V 锂电池进行充电&#xff0c;电流最高可配置1A。放电工作时&#xff0c;采用开关频率 1MHz同步降压转换器进行放电&#xff0c;放电电流可以达到3…

Flink往Starrocks写数据报错:too many filtered rows

Bug信息 Caused by: com.starrocks.data.load.stream.exception.StreamLoadFailException: {"TxnId": 2711690,"Label": "cd528707-8595-4a35-b2bc-39b21087d6ec","Status": "Fail","Message": "too many f…

iphone15 nplayer播放本地电影投屏天猫魔盒(电视)卡顿解决方案

文章目录 投屏环境现象写在前面 解决方案所需投屏app安装方法试用结果如果文章对您有用&#xff0c;欢迎收藏或关注&#xff01; iphone15 nplayer播放本地电影投屏天猫魔盒(电视)卡顿解决方案 投屏环境 全千兆wifi6局域网 1000兆电信宽带 天猫魔盒4Pro 8G&#xff08;M19&…

数据集:机器人理解世界的关键

原创 | 文 BFT机器人 传统的机器人和工业自动化解决方案已经颇有成效。在工厂中入驻自动化机器人可以快速地帮助工人们完成长时间重复劳动的任务。随着用工成本上涨、技能人才短缺、工作环境恶劣等问题的凸显&#xff0c;社会更迫切地需要采用自动化设备代替人工来完成该类操作…

红黑树——原理刨析

众所周知&#xff0c;红黑树是从AVLTree树中衍变而来的&#xff0c;所以在学红黑树之前还是要好好的理解一下AVLTree树的原理&#xff0c;为理解红黑树减轻理解负担&#xff0c;好了进入正题。 红黑树原理&#xff1a; 由名可知&#xff0c;红黑树——肯定是与颜色有关的一个树…

通讯录详解(静态版,动态版,文件版)

&#x1f493;博客主页&#xff1a;江池俊的博客⏩收录专栏&#xff1a;C语言进阶之路&#x1f449;专栏推荐&#xff1a;✅C语言初阶之路 ✅数据结构探索✅C语言刷题专栏&#x1f4bb;代码仓库&#xff1a;江池俊的代码仓库&#x1f389;欢迎大家点赞&#x1f44d;评论&#x…

第三章:boundary-value analysis

文章目录 Boundary-value Analysiscomputational faults 计算错误boundary shift 边界偏移boundary value analysis 的优势Path condition, domain, and domain boundary (路径条件、域和域边界)Open and closed boundaries (闭合边界 / 开放边界)on / off pointGuidelinestr…

浅析LiveMedia智能视频网关的AI识别技术及应用场景

一、行业背景 &#xff08;1&#xff09;AI技术在安防领域大量落地应用 随着近几年人工智能的快速发展&#xff0c;深度学习方法及性能日益提升&#xff0c;计算机视觉、图像处理、视频结构化和大数据分析等技术也不断完善&#xff0c;使得安防产品逐步走向智能化。在技术成熟…

redis五种数据类型

Redis支持五种数据类型&#xff1a;string&#xff08;字符串&#xff09;&#xff0c;hash&#xff08;哈希&#xff09;&#xff0c;list&#xff08;列表&#xff09;&#xff0c;set&#xff08;集合&#xff09;及zset(sorted set&#xff1a;有序集合)。 1.String&#…

Rust结构体和枚举类

文章目录 元组结构体结构体枚举类 Rust初步上手⚙所有权 元组结构体 元组结构体是最简单的结构体&#xff0c;可以粗暴地理解为是有名字的元组&#xff0c;二者的区别如下。 let tup: (i32, f64, u8) (500, 6.4, 1);struct Test(i32, f64, u8); let t Test(500,6.4,1)第一…