SpringCloud 微服务全栈体系(十四)

第十一章 分布式搜索引擎 elasticsearch

四、RestAPI

  • ES 官方提供了各种不同语言的客户端,用来操作 ES。这些客户端的本质就是组装 DSL 语句,通过 http 请求发送给 ES。官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html

  • 其中的 Java Rest Client 又包括两种:

    • Java Low Level Rest Client
    • Java High Level Rest Client
  • 此处使用的是 Java HighLevel Rest Client 客户端 API

1. 导入 Demo 工程

1.1 导入数据
  • 导入资料提供的数据库数据:
    (见专栏 -> 全栈资料包 -> 资源包/02_cloud)
  • 数据结构如下:
sql 复制代码
CREATE TABLE `tb_hotel` (
  `id` bigint(20) NOT NULL COMMENT '酒店id',
  `name` varchar(255) NOT NULL COMMENT '酒店名称;例:7天酒店',
  `address` varchar(255) NOT NULL COMMENT '酒店地址;例:航头路',
  `price` int(10) NOT NULL COMMENT '酒店价格;例:329',
  `score` int(2) NOT NULL COMMENT '酒店评分;例:45,就是4.5分',
  `brand` varchar(32) NOT NULL COMMENT '酒店品牌;例:如家',
  `city` varchar(32) NOT NULL COMMENT '所在城市;例:上海',
  `star_name` varchar(16) DEFAULT NULL COMMENT '酒店星级,从低到高分别是:1星到5星,1钻到5钻',
  `business` varchar(255) DEFAULT NULL COMMENT '商圈;例:虹桥',
  `latitude` varchar(32) NOT NULL COMMENT '纬度;例:31.2497',
  `longitude` varchar(32) NOT NULL COMMENT '经度;例:120.3925',
  `pic` varchar(255) DEFAULT NULL COMMENT '酒店图片;例:/img/1.jpg',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1.2 导入项目
  • 导入资料提供的项目:
    (见专栏 -> 全栈资料包 -> 资源包/02_cloud)
1.3 mapping 映射分析
  • 创建索引库,最关键的是 mapping 映射,而 mapping 映射要考虑的信息包括:

    • 字段名
    • 字段数据类型
    • 是否参与搜索
    • 是否需要分词
    • 如果分词,分词器是什么?
  • 其中:

    • 字段名、字段数据类型,可以参考数据表结构的名称和类型
    • 是否参与搜索要分析业务来判断,例如图片地址,就无需参与搜索
    • 是否分词呢要看内容,内容如果是一个整体就无需分词,反之则要分词
    • 分词器,我们可以统一使用 ik_max_word
  • 看下酒店数据的索引库结构:

json 复制代码
PUT /hotel
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword",
        "index": false
      },
      "price":{
        "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
        "type": "keyword",
        "copy_to": "all"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword"
      },
      "location":{
        "type": "geo_point"
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}
  • 几个特殊字段说明:

    • location:地理坐标,里面包含精度、纬度
    • all:一个组合字段,其目的是将多字段的值利用 copy_to 合并,提供给用户搜索
  • 地理坐标说明:

  • copy_to 说明:
1.4 初始化 RestClient
  • 在 elasticsearch 提供的 API 中,与 elasticsearch 一切交互都封装在一个名为 RestHighLevelClient 的类中,必须先完成这个对象的初始化,建立与 elasticsearch 的连接。

  • 分为三步:

1)引入 es 的 RestHighLevelClient 依赖:

xml 复制代码
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

2)因为 SpringBoot 默认的 ES 版本是 7.6.2,所以我们需要覆盖默认的 ES 版本:

xml 复制代码
<properties>
    <java.version>1.8</java.version>
    <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

3)初始化 RestHighLevelClient:

  • 初始化的代码如下:
java 复制代码
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        HttpHost.create("http://192.168.150.101:9200")
));
  • 这里为了单元测试方便,我们创建一个测试类 HotelIndexTest,然后将初始化的代码编写在@BeforeEach 方法中:
java 复制代码
package com.alex.hotel;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

public class HotelIndexTest {
    private RestHighLevelClient client;

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

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

2. 创建索引库

2.1 代码解读
  • 创建索引库的 API 如下:
  • 代码分为三步:

    • 创建 Request 对象。因为是创建索引库的操作,因此 Request 是 CreateIndexRequest。
    • 添加请求参数,其实就是 DSL 的 JSON 参数部分。因为 json 字符串很长,这里是定义了静态字符串常量 MAPPING_TEMPLATE,让代码看起来更加优雅。
    • 发送请求,client.indices()方法的返回值是 IndicesClient 类型,封装了所有与索引库操作有关的方法。
2.2 完整示例
  • 在 hotel-demo 的 com.alex.hotel.constants 包下,创建一个类,定义 mapping 映射的 JSON 字符串常量:
java 复制代码
package com.alex.hotel.constants;

public class HotelConstants {
    public static final String MAPPING_TEMPLATE = "{\n" +
            "  \"mappings\": {\n" +
            "    \"properties\": {\n" +
            "      \"id\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"name\":{\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"address\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"price\":{\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"score\":{\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"brand\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"city\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"starName\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"business\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"location\":{\n" +
            "        \"type\": \"geo_point\"\n" +
            "      },\n" +
            "      \"pic\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"all\":{\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";
}
  • 在 hotel-demo 中的 HotelIndexTest 测试类中,编写单元测试,实现创建索引:
java 复制代码
@Test
void createHotelIndex() throws IOException {
    // 1.创建Request对象
    CreateIndexRequest request = new CreateIndexRequest("hotel");
    // 2.准备请求的参数:DSL语句
    request.source(MAPPING_TEMPLATE, XContentType.JSON);
    // 3.发送请求
    client.indices().create(request, RequestOptions.DEFAULT);
}

3. 删除索引库

  • 删除索引库的 DSL 语句非常简单:
json 复制代码
DELETE /hotel
  • 与创建索引库相比:

    • 请求方式从 PUT 变为 DELTE
    • 请求路径不变
    • 无请求参数
  • 所以代码的差异,注意体现在 Request 对象上。依然是三步走:

    • 创建 Request 对象。这次是 DeleteIndexRequest 对象
    • 准备参数。这里是无参
    • 发送请求。改用 delete 方法
  • 在 hotel-demo 中的 HotelIndexTest 测试类中,编写单元测试,实现删除索引:

java 复制代码
@Test
void testDeleteHotelIndex() throws IOException {
    // 1.创建Request对象
    DeleteIndexRequest request = new DeleteIndexRequest("hotel");
    // 2.发送请求
    client.indices().delete(request, RequestOptions.DEFAULT);
}

4. 判断索引库是否存在

  • 判断索引库是否存在,本质就是查询,对应的 DSL 是:
json 复制代码
GET /hotel
  • 因此与删除的 Java 代码流程是类似的。依然是三步走:

    • 创建 Request 对象。这次是 GetIndexRequest 对象
    • 准备参数。这里是无参
    • 发送请求。改用 exists 方法
java 复制代码
@Test
void testExistsHotelIndex() throws IOException {
    // 1.创建Request对象
    GetIndexRequest request = new GetIndexRequest("hotel");
    // 2.发送请求
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    // 3.输出
    System.err.println(exists ? "索引库已经存在!" : "索引库不存在!");
}

5. 总结

  • JavaRestClient 操作 elasticsearch 的流程基本类似。核心是 client.indices()方法来获取索引库的操作对象。

  • 索引库操作的基本步骤:

    • 初始化 RestHighLevelClient
    • 创建 XxxIndexRequest。XXX 是 Create、Get、Delete
    • 准备 DSL( Create 时需要,其它是无参)
    • 发送请求。调用 RestHighLevelClient#indices().xxx()方法,xxx 是 create、exists、delete
相关推荐
Flynn Liang24 分钟前
保姆级|Spring AI 2.0 第一个 Demo:Spring Boot 4 接入大模型(含国内可用方案 + 完整 pom)
人工智能·spring·ollama·springai·deepseek·springai入门·java接入大模型
刘老高1 小时前
微服务架构实战:ShardingSphere Proxy + Canal 分库分表与 ES 同步全链路
elasticsearch·微服务·架构
会编程的吕洞宾1 小时前
Spring AI 语义缓存:重复提问不重复花钱
java·人工智能·后端·spring
郭小李1 小时前
spring @RequestMapping注解
java·后端·spring
CarIise12 小时前
SpringMVC 异常处理与拦截器
大数据·spring
知守观13 小时前
设计模式实战:从真实老项目中看标准模板方法与工厂的落地
java·spring·设计模式
坐看风云变幻15 小时前
《系统架构师成长之路》第 8 期|微服务:单体拆分的时机、粒度与通信
微服务·架构·系统架构
\光辉岁月/15 小时前
3.spring5-AOP
spring·代理模式·aop·spring5
东华果汁哥16 小时前
【Spring AI 实战】从零开始调用大模型接口,构建 Java AI 应用
java·人工智能·spring
Song_RsHt16 小时前
Redis 与微服务,什么场景下无可替代
java·redis·后端·学习·微服务