Spring Boot Elasticsearch

时间:2020-02-23 14:35:47  来源:igfitidea点击:

Spring Boot Elasticsearch 6

在本文中,我们将设置一个示例Spring boot Elasticsearch应用程序。
我们将使用最新版本的Elasticsearch,即6.1.x。
为了与Elasticsearch搜索引擎进行交互,我们将使用Elasticsearch Rest客户端。
我们不使用Spring Data ElasticSearch,因为它不支持最新的ElasticSearch版本(即6.x)。

设置项目

Maven依赖

我们将为此项目使用Maven构建系统,这是我们使用的依赖项:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
<!-- Elasticsearch Dependencies -->
<dependency>
  <groupId>org.elasticsearch</groupId>
  <artifactId>elasticsearch</artifactId>
  <version>6.1.2</version>
</dependency>
<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-high-level-client</artifactId>
  <version>6.1.2</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.8.2</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.8.2</version>
</dependency>
<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-client</artifactId>
  <version>6.1.2</version>
</dependency>
<dependency>
  <groupId>org.elasticsearch.client</groupId>
  <artifactId>elasticsearch-rest-client-sniffer</artifactId>
  <version>6.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
  <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
  </plugin>
</plugins>
</build>

确保从Maven Central使用稳定版本的Spring Boot。

Elasticsearch配置

现在,我们将必须在应用程序中配置ElasticSearch。
让我们分为两个部分。
首先,我们将在" application.properties"文件中提供Elasticsearch地址:

spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=elasticsearch

我们仅在此处提供了Elasticsearch集群名称和节点名称,而这些实际上是默认值。
现在,是时候在Java配置类中使用这些值了。

package com.theitroad.elasticsearch.config;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ElasticSearchConfiguration extends AbstractFactoryBean {

  private static final Logger LOG = LoggerFactory.getLogger(ElasticSearchConfiguration.class);
  @Value("${spring.data.elasticsearch.cluster-nodes}")
  private String clusterNodes;
  @Value("${spring.data.elasticsearch.cluster-name}")
  private String clusterName;
  private RestHighLevelClient restHighLevelClient;

  @Override
  public void destroy() {
      try {
          if (restHighLevelClient != null) {
              restHighLevelClient.close();
          }
      } catch (final Exception e) {
          LOG.error("Error closing ElasticSearch client: ", e);
      }
  }

  @Override
  public Class<RestHighLevelClient> getObjectType() {
      return RestHighLevelClient.class;
  }

  @Override
  public boolean isSingleton() {
      return false;
  }

  @Override
  public RestHighLevelClient createInstance() {
      return buildClient();
  }

  private RestHighLevelClient buildClient() {
      try {
          restHighLevelClient = new RestHighLevelClient(
                  RestClient.builder(
                          new HttpHost("localhost", 9200, "http"),
                          new HttpHost("localhost", 9201, "http")));
      } catch (Exception e) {
          LOG.error(e.getMessage());
      }
      return restHighLevelClient;
  }
}

通过这种配置,我们确保了ElasticSearch能够使用Rest Client API与其服务器成功建立连接。

使用应用程序

现在就开始放置应用程序的工作组件。

应用程式模型

我们将以一个简单的模型作为用户:

package com.theitroad.elasticsearch.model;

import com.fasterxml.Hymanson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Book {

  private String id;
  private String title;
  private String author;
  private float price;

  //standard setters and getters
}

我们将在应用程序中进行以下功能和数据库交互:

  • 取得ID为一本书的书
  • 插入一本书
  • 更新一本书
  • 删除书籍

定义控制器

让我们开始制作控制器:

package com.theitroad.elasticsearch.controller;

import com.theitroad.elasticsearch.model.Book;
import com.theitroad.elasticsearch.dao.BookDao;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/books")
public class BookController {

  private BookDao bookDao;

  public BookController(BookDao bookDao) {
      this.bookDao = bookDao;
  }
...
}

我们只是自动装配DAO依赖项,接下来将使用它。

定义API

对于我们提到的功能,我们现在将制作API并访问内部将使用Elasticsearch Rest Client API的DAO依赖项。

取得ID为一本书的书

让我们得到一本ID为的书:

@GetMapping("/{id}")
public Map<String, Object> getBookById(@PathVariable String id){
return bookDao.getBookById(id);
}

插入一本书

现在,让我们现在插入一本书:

@PostMapping
public Book insertBook(@RequestBody Book book) throws Exception {
return bookDao.insertBook(book);
}

更新一本书

我们将在以下片段中更新一本书:

@PutMapping("/{id}")
public Map<String, Object> updateBookById(@RequestBody Book book, @PathVariable String id) {
return bookDao.updateBookById(id, book);
}

删除书

现在,我们已将示例数据添加到数据库中,让我们尝试提取其中的一部分:

@DeleteMapping("/{id}")
public void deleteBookById(@PathVariable String id) {
bookDao.deleteBookById(id);
}

这是Spring Data API为我们提供的便利,但是它也有一些缺点。
当我们还定义了ElasticsearchTemplate版本时,我们将详细说明。
让我们也开始吧。

定义DAO层

现在,我们将实际定义实现这些目标的DAL查询。

定义DAO

我们将从提及我们需要的依赖关系开始:

package com.theitroad.elasticsearch.dao;

import com.theitroad.elasticsearch.model.Book;
import com.fasterxml.Hymanson.core.JsonProcessingException;
import com.fasterxml.Hymanson.databind.ObjectMapper;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.stereotype.Repository;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Repository
public class BookDao {

private final String INDEX = "bookdata";
private final String TYPE = "books";  
private RestHighLevelClient restHighLevelClient;
private ObjectMapper objectMapper;

public BookDao( ObjectMapper objectMapper, RestHighLevelClient restHighLevelClient) {
  this.objectMapper = objectMapper;
  this.restHighLevelClient = restHighLevelClient;
}
...
}

插入查询

我们将从在ES中插入一本书开始:

public Book insertBook(Book book){
book.setId(UUID.randomUUID().toString());
Map dataMap = objectMapper.convertValue(book, Map.class);
IndexRequest indexRequest = new IndexRequest(INDEX, TYPE, book.getId())
              .source(dataMap);
try {
  IndexResponse response = restHighLevelClient.index(indexRequest);
} catch(ElasticsearchException e) {
  e.getDetailedMessage();
} catch (java.io.IOException ex){
  ex.getLocalizedMessage();
}
return book;
}

显然,在将模型数据插入ES数据库之前,需要将其转换为Map数据结构。

搜索查询

现在,我们将搜索ID为:的图书:

public Map<String, Object> getBookById(String id){
GetRequest getRequest = new GetRequest(INDEX, TYPE, id);
GetResponse getResponse = null;
try {
  getResponse = restHighLevelClient.get(getRequest);
} catch (java.io.IOException e){
  e.getLocalizedMessage();
}
Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
return sourceAsMap;
}

在此,要注意的是,数据也作为地图数据结构被搜索并返回。
这是我们需要处理的事情,将数据转换为Map来进行数据库事务。

public Map<String, Object> updateBookById(String id, Book book){
UpdateRequest updateRequest = new UpdateRequest(INDEX, TYPE, id)
        .fetchSource(true);    //Fetch Object after its update
Map<String, Object> error = new HashMap<>();
error.put("Error", "Unable to update book");
try {
  String bookJson = objectMapper.writeValueAsString(book);
  updateRequest.doc(bookJson, XContentType.JSON);
  UpdateResponse updateResponse = restHighLevelClient.update(updateRequest);
  Map<String, Object> sourceAsMap = updateResponse.getGetResult().sourceAsMap();
  return sourceAsMap;
}catch (JsonProcessingException e){
  e.getMessage();
} catch (java.io.IOException e){
  e.getLocalizedMessage();
}
return error;
}

删除资料

现在,我们将搜索具有ID的图书:

public void deleteBookById(String id) {
DeleteRequest deleteRequest = new DeleteRequest(INDEX, TYPE, id);
try {
  DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest);
} catch (java.io.IOException e){
  e.getLocalizedMessage();
}
}

我认为删除对象的查询最简单。
现在,通过运行它来尝试该应用程序。

运行应用程序

我们只需使用一个命令即可运行此应用程序:

mvn spring-boot:run

应用程序运行后,我们可以尝试使用此API保存新书。
在此之前,只需使用以下API确认ES正在运行:

127.0.0.1:9200

我们将收到以下响应:现在,我们使用以下API插入数据:

127.0.0.1:8080/books

我们使用带有JSON的POST请求:

{
"title" : "Java Always",
"author" : "theitroad",
"price" : 99.1
}

我们的回应如下:让我们尝试另一个请求,以获取具有上述ID的图书。
我们将在此API上使用GET请求:

127.0.0.1:8080/books/55c200ff-9674-44aa-8779-a0f3ff925e74