【数据篇】SpringBoot 整合 Elasticsearch 实践数据搜索引擎

398 阅读4分钟

写在最前

版本选择

Spring Data Release TrainSpring Data ElasticsearchElasticsearchSpring FrameworkSpring Boot
2021.1 (Q)4.3.x7.15.25.3.x2.5.x
2021.0 (Pascal)4.2.x7.12.05.3.x2.5.x
2020.0 (Ockham)4.1.x7.9.35.3.22.4.x
Neumann4.0.x7.6.25.2.122.3.x
Moore3.2.x6.8.125.2.122.2.x
Lovelace3.1.x6.2.25.1.192.1.x
Kay3.0.x5.5.05.0.132.0.x
Ingalls2.1.x2.4.04.3.251.5.x

SpringBoot 整合 Elasticsearch

Demo 使用 Spring Boot 使用 2.6.6 版本,默认对应 Elasticsearch 7.15.2(环境安装的 7.17.2)。Deom 地址:mingyue-springboot-elasticsearch

1.添加依赖

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

2.修改配置文件

elastic:
  address: http://ip:9200

3.添加 Elasticsearch 配置类

import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;

/**
 * Elasticsearch 配置类
 *
 * @author Strive
 */
@Slf4j
@Configuration
public class ElasticConfig extends AbstractElasticsearchConfiguration {
  @Value("${elastic.address}")
  private List<String> addressList;

  @Bean
  @Override
  public RestHighLevelClient elasticsearchClient() {
    HttpHost[] httpHosts = new HttpHost[addressList.size()];

    for (int i = 0; i < addressList.size(); i++) {
      String address = addressList.get(i);

      log.info("create elastic host:{}", address);
      httpHosts[i] = HttpHost.create(address);
    }
    return new RestHighLevelClient(RestClient.builder(httpHosts));
  }
}

4.测试连接

import java.io.IOException;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @author Strive
 * @date 2022/4/20 10:35
 * @description
 */
@SpringBootTest
public class ElasticsearchTest {

  @Autowired
  @Qualifier("elasticsearchClient")
  public RestHighLevelClient client;

  /** 创建索引测试 */
  @Test
  public void createIndexTest() throws IOException {
    CreateIndexRequest request = new CreateIndexRequest("mingyue");
    CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);

    // 查看是否创建成功
    System.out.println(response.isAcknowledged());
    client.close();
  }
}

执行日志如下:

true

访问:http://ip:5601/app/management/data/index_management/indices

mingyue 索引存在即可!

image-20220425210940659

常用注解

@Document

标示映射到 Elasticsearch 文档上的领域对象。

public @interface Document {
  	//索引库名次,mysql中数据库的概念
	String indexName();
  	//文档类型,mysql中表的概念
	String type() default "";
  	//默认分片数
	short shards() default 5;
  	// 默认副本数量
	short replicas() default 1;
}

@Id

表示是文档的 id,文档可以认为是 mysql 中表行的概念。

@Field

文档中字段说明。

public @interface Field {
  	// 文档中字段的类型
	FieldType type() default FieldType.Auto;
  	// 是否建立倒排索引
	boolean index() default true;
  	// 是否进行存储
	boolean store() default false;
  	// 分词器名次
	String analyzer() default "";
}
// 为文档自动指定元数据类型
public enum FieldType {
    // 会进行分词并建了索引的字符类型
	Text,
	Integer,
	Long,
	Date,
	Float,
	Double,
	Boolean,
	Object,
    // 自动判断字段类型
	Auto,
    // 嵌套对象类型
	Nested,
	Ip,
	Attachment,
    // 不会进行分词建立索引的类型
	Keyword
}

实践数据搜索

古诗词检索,Demo 地址:mingyue-springboot-elasticsearch

1.定义 AncientPoetry 实体

analyzer = "ik_max_word" 需要 elasticsearch 安装 IK 分词器

import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

/**
 * 古诗词
 *
 * @author Strive
 */
@Data
@ToString
@Document(indexName = "ancient_poetry")
public class AncientPoetry implements Serializable {

  @Id
  @Field(type = FieldType.Text)
  private String id;

  @Field(type = FieldType.Text, analyzer = "ik_max_word")
  private String title;

  @Field(type = FieldType.Text, analyzer = "ik_max_word")
  private String author;

  @Field(type = FieldType.Text, analyzer = "ik_max_word")
  private String content;

  @Field(type = FieldType.Date, format = DateFormat.basic_date_time)
  private Date createTime;
}

2.创建接口 ESAncientPoetryRepository

该接口继承了 ElasticsearchRepository 接口,ElasticsearchRepository 接口定义了 Elasticsearch 的 CRUD,继承了该接口的接口甚至无需定义任何其他的方法就能满足基本需求。最牛的是通过定义的方法名就能自动创建各种查询!!!

import com.csp.mingyue.es.model.AncientPoetry;
import java.util.List;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**
 * 古诗词 ES 该接口继承了ElasticsearchRepository接口,ElasticsearchRepository接口定义了Elasticsearch的CRUD,
 * 继承了该接口的接口甚至无需定义任何其他的方法就能满足基本需求。
 *
 * @author Strive
 * @date 2022/4/26 09:46
 * @description
 */
public interface EsAncientPoetryRepository extends ElasticsearchRepository<AncientPoetry, String> {
  /**
   * 关键字检索 标题 或 内容
   *
   * @param title 标题
   * @param content 内容
   * @return 古诗词列表
   */
  List<AncientPoetry> findByTitleOrContent(String title, String content);
}

3.编写 Service

import com.csp.mingyue.es.model.AncientPoetry;
import com.csp.mingyue.es.repository.EsAncientPoetryRepository;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * ESAncientPoetry service 层
 *
 * @author Strive
 * @date 2022/4/26 10:07
 */
@Service
@RequiredArgsConstructor
public class EsAncientPoetryService {

  private final EsAncientPoetryRepository esAncientPoetryRepository;

  /**
   * 添加古诗词到ES
   *
   * @param ancientPoetry 古诗词
   */
  public boolean addAncientPoetry(AncientPoetry ancientPoetry) {
    esAncientPoetryRepository.save(ancientPoetry);
    return Boolean.TRUE;
  }

  /**
   * 根据ID查询古诗词
   *
   * @param id 古诗词ID
   */
  public AncientPoetry getById(String id) {
    Optional<AncientPoetry> ancientPoetryOptional = esAncientPoetryRepository.findById(id);

    return ancientPoetryOptional.orElse(null);
  }

  /**
   * 关键字检索 标题 或 内容
   *
   * @param keyword 关键字
   */
  public List<AncientPoetry> findAncientPoetry(String keyword) {
    return esAncientPoetryRepository.findByTitleOrContent(keyword, keyword);
  }
}

4.编写接口

import com.csp.mingyue.es.model.AncientPoetry;
import com.csp.mingyue.es.service.EsAncientPoetryService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * ES 操作接口
 *
 * @author Strive
 * @date 2022/4/26 09:52
 * @description
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/es/ancientPoetry")
public class EsAncientPoetryController {

  private final EsAncientPoetryService esAncientPoetryService;

  @PostMapping
  public ResponseEntity<Boolean> add(@RequestBody AncientPoetry ancientPoetry) {
    return ResponseEntity.ok(esAncientPoetryService.addAncientPoetry(ancientPoetry));
  }

  @GetMapping("{id}")
  public ResponseEntity<AncientPoetry> get(@PathVariable String id) {
    return ResponseEntity.ok(esAncientPoetryService.getById(id));
  }

  @GetMapping("/search")
  public ResponseEntity<List<AncientPoetry>> findAncientPoetry(String keyword) {
    return ResponseEntity.ok(esAncientPoetryService.findAncientPoetry(keyword));
  }
}

5.测试

启动项目后,查看 kibana 是否有 ancient_poetry 索引

添加古诗词

POST http://127.0.0.1:8080/es/ancientPoetry

Body 类型 : application/json

{
    "id": "1",
    "title": "水调歌头",
    "author": "苏轼",
    "content": "明月几时有,把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。",
    "createTime": "2022-04-26"
}

根据用户ID获取古诗词

GET http://127.0.0.1:8080/es/ancientPoetry/1

检索古诗词

GET http://127.0.0.1:8080/es/ancientPoetry/search?keyword=明月

[    {        "id": "2",        "title": "西江月·夜行黄沙道中",        "author": "辛弃疾",        "content": "明月别枝惊鹊,清风半夜鸣蝉。稻花香里说丰年,听取蛙声一片。",        "createTime": "2022-04-26T00:00:00.000+00:00"    },    {        "id": "1",        "title": "水调歌头",        "author": "苏轼",        "content": "明月几时有,把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。",        "createTime": "2022-04-26T00:00:00.000+00:00"    }]