搭建ES项目、使用java代码。可选的客户端有RestHighLevelClient、Spring Data Elasticsearch和Jest,本章将用一个搜索需求分别进行介绍。
1.1 Kibana客户端简介
Kibana是ELK家族中一个开源、免费的可视化数据搜索和分析平台。借助Kibana,用户不需要编码就可以将ES中分析的结果进行可视化呈现,如以常用的饼图、柱状图和时序图等方式呈现。除了可视化数据分析功能,Kibana还提供了Dev Tools,它是一款可以与ES进行交互式请求的工具,可以借助它进行DSL调试。限于篇幅,本节只介绍Kibana搭配单机版ES的配置方法
1.2 Kibana安装
注意:最好保持和es版本一致。
下载地址:https://www.elastic.co/cn/downloads/kibana 版本直接下载:https://artifacts.elastic.co/downloads/kibana/kibana-7.10.2-windows-x86_64.zip
- 下载完成直接解压
- 修改kibana文件夹下config/kibana.yml 把es的访问地址给写上 elasticsearch.hosts: [“http://localhost:9200”]
- 访问地址 http:127.0.0.1:5601
- 点击Dev Tools 目前的工具包含
- Console:提供ES Rest形式交互的控制台
- Search Profiler:可以分析ES响应某一请求的耗时
- GROK DeBugger:提供GROK语言的调试器,联合使用logstash上传数据时,用作调试分割文本的表达式
- Painless Lab:提供Painless语言的调试器,用户在自定义排序脚本代码时可以使用
- 通过Kibana客户查询数据
2. Java客户端简介
ES 和客户端的通信是根据Http来通信的。用户可以通过任意语言来进行通信。例如:java、Python等。java语言已经支持了很多的API只要拿来使用即可。
2.1 创建SpringBoot项目 导入es相关联的jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xiaobai</groupId>
<artifactId>es</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>es</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.2</version>
</dependency>
<!--ES依赖-->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.10.2</version>
</dependency>
<!-- springBoot-data-Es 依赖-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-elasticsearch</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2 application.yml
elasticsearch:
rest:
hosts: 127.0.0.1:9200
2.3 创建ES client端Bean
- 无安全验证玩法
package com.example.xiaobai.es.resthightlevel;
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 java.util.Arrays;
import java.util.Objects;
@Configuration
public class EsClient {
@Value("${elasticsearch.rest.hosts:}") //读取ES主机+端口配置
private String hosts;
@Bean(value = "restHighLevelClient")
public RestHighLevelClient initSimpleClient() {
//根据配置文件配置HttpHost数组
HttpHost[] httpHosts = Arrays.stream(hosts.split(",")).map(
host -> {
//分隔ES服务器的IP和端口
String[] hostParts = host.split(":");
String hostName = hostParts[0];
int port = Integer.parseInt(hostParts[1]);
return new HttpHost(hostName, port, HttpHost.DEFAULT_SCHEME_NAME);
}).filter(Objects::nonNull).toArray(HttpHost[]::new);
//构建客户端
return new RestHighLevelClient(RestClient.builder(httpHosts));
}
}
- 有安全验证玩法(生产)
@Bean(value = "restHighLevelClient")
public RestHighLevelClient initSimpleClient1() {
//根据配置文件配置HttpHost数组
HttpHost[] httpHosts = Arrays.stream(hosts.split(",")).map(
host -> {
//分隔ES服务器的IP和端口
String[] hostParts = host.split(":");
String hostName = hostParts[0];
int port = Integer.parseInt(hostParts[1]);
return new HttpHost(hostName, port, HttpHost.DEFAULT_SCHEME_NAME);
}).filter(Objects::nonNull).toArray(HttpHost[]::new);
// 生成凭证
final BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
basicCredentialsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials(esUser,esPassword));
//构建客户端
return new RestHighLevelClient(RestClient.builder(httpHosts).setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpAsyncClientBuilder) {
return httpAsyncClientBuilder.setDefaultCredentialsProvider(basicCredentialsProvider);
}
}));
}
2.4 创建创建酒店对象pojo
package com.xiaobai.es.pojo;
import lombok.Data;
@Data
public class Hotel {
/**
* 对应文档id
*/
private String id;
/**
* 索引名称
*/
private String index;
/**
* 分数
*/
private Float score;
/**
* 酒店 名称
*/
private String title;
/**
* 酒店城市
*/
private String city;
/**
* 酒店价格
*/
private Double price;
}
2.5 创建service类
package com.xiaobai.es.service;
import com.xiaobai.es.pojo.Hotel;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class EsService {
@Resource
private RestHighLevelClient restHighLevelClient;
private final static String INDEX_NAME = "hoteld";
public List<Hotel> getHotelFromTitle(String keyword){
// 客户端请求 创建请求对象 传入索引名称
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
// 构建查询条件对象
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 书写查询语句
searchSourceBuilder.query(QueryBuilders.matchQuery("title",keyword));
// 把查询语句设置给请求对象
searchRequest.source(searchSourceBuilder);
List<Hotel> hotels = new ArrayList<>();
// 查询
try {
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
RestStatus status = searchResponse.status();
if (status != RestStatus.OK){
return hotels;
}
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
Hotel hotel = new Hotel();
hotel.setId(hit.getId());
hotel.setIndex(hit.getIndex());
hotel.setScore(hit.getScore());
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
hotel.setTitle((String) sourceAsMap.get("title"));
hotel.setCity((String) sourceAsMap.get("city"));
hotel.setPrice((Double) sourceAsMap.get("price"));
hotels.add(hotel);
}
} catch (IOException e) {
e.printStackTrace();
}
return hotels;
}
}
- 首先创建SearchRequest对象 传入要查询的索引名称hoteld。
- 创建承载的查询条件对象SearchSourceBuilder
- 传入QueryBuilder对象传入查询条件对象。本次是根据九点名称来模糊查询。
- 请求对象设置查询条件对象。 searchRequest.source(searchSourceBuilder);
- 通过restHighLevelClient查询。
- SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
- searchResponse.getHits() 返回了所有查询结果的封装数据。
- 遍历Hits 可得到Hit。这里面可以获取到对应的索引名称、分数、文档id等
- hit.getSourceAsMap()把文档内容转换成map的方式便于获取。但是可能会有null指针的情况。
2.6 elasticsearch-rest-high-level-client
- 这种方式配置起来比较复杂。
- 每次查询的时候都是根据rest http的方式去调用es服务。每次都需要建立链接
- 查询信息和转换数据比较麻烦
3. Spring-Data-Elasticsearch 方式
spring-data-elasticsearch是和springBoot中的一套组件。可以和es服务保持长链接,不需要每次查询的时候需要建立网络链接了。并且可以通过Repository接口自动实现,可以通过方法名的语义实现查询功能。而且查询到数据后可以直接封装到自定义pojo中。方便后续对数据的二次加工。
3.1 创建SpringBoot客户端
3.1.1导入相关jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.8</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sping.data</groupId>
<artifactId>springdata</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springdata</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.1.2 配置application.yml
spring:
elasticsearch:
rest:
uris: http://127.0.0.1:9200
username: elastic
password: password
如果想要配置多个节点只需要uris 配置多个ip即可。中间用逗号分割。
3.1.3 使用spring-data-elasticsearchl来根据"再来"查询符合的酒店
3.1.3.1创建pojo类
@Data
@Document(indexName = "hoteld")
public class Hotel1 {
@Id
private String id;
private String title;
private String city;
private Double price;
}
@Id 代表是主键id 级文档id
3.1.3.2 创建Repository对象
/**
* @author liruiqing
*/
public interface EsRepository extends CrudRepository<Hotel1,String> {
/**
* 根据 title 查询符合的酒店
* @param title
* @return
*/
List<Hotel1> findByTitleLike(String title);
}
以方法名语义的方式实现查询数据。
需要继承CrudRepository<o1,o2> o1 为pojo类 o2为文档id类型
3.1.3.3 service类
@Service
public class EsService {
@Autowired
private EsRepository esRepository;
public List<Hotel1> findByTitleLike(String keyword){
List<Hotel1> byTitleLike = esRepository.findByTitleLike(keyword);
return byTitleLike;
}
}
3.1.3.4 controller类
@RestController
public class TestController {
@Autowired
private EsService esService;
@RequestMapping("/test")
public String getHotelStr(){
List<Hotel1> hotels = esService.findByTitleLike("再来");
if (hotels.isEmpty()){
return "no data";
}
return hotels.toString();
}
}
4. Jest客户端搜索文档
Jest是为springBoot项目是低版本时做兼容时用到的,如果SpringBoot的版本过高的话application.yml中配置信息已经不生效了,被移除了。下面的版本是可以使用的。
4.1 配置项
4.1.1 导入相关jar包
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository springboot版本-->
</parent>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Jest依赖-->
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
<version>6.3.1</version>
</dependency>
<!--简化开发组件Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--ES依赖-->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.10.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.8</version>
<scope>compile</scope>
</dependency>
</dependencies>
4.1.2 配置application.yml
spring:
elasticsearch:
jest:
uris: http://127.0.0.1:9200
username: elastic
password: password
server:
port: 8082
4.2 实现
4.2.1 Pojo类
@Data
public class Hotel {
@JestId // 文档id
private String id;
private String title;
private String city;
private Double price;
}
4.2.2 service类
@Service
public class EsService {
@Resource
private JestClient jestClient;
public List<Hotel> findByTitleLike(String keyword){
// 创建sql语句对象 本次为查询语句
MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("title", keyword);
// 设置查询builder对象 并把查询对象设置进去
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchQuery);
// 调用search.Builder把查询的builder对象字符串给设置进去 addIndex是索引名称
Search search = new Search.Builder(searchSourceBuilder.toString()).addIndex("hoteld").build();
try {
// 使用jestClient.execute()方法获取searchResult数据对象 并把返回的数据集通过getSourceAsObjectList转换为集合
SearchResult searchResult = jestClient.execute(search);
if (searchResult.isSucceeded()){
List<Hotel> sourceAsObjectList = searchResult.getSourceAsObjectList(Hotel.class);
return sourceAsObjectList;
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
4.2.3 Controller类
@RestController
public class TestController {
@Autowired
private EsService esService;
@RequestMapping("/test")
public String findByTitleLike(){
List<Hotel> hotels = esService.findByTitleLike("再来");
if (hotels == null || hotels.isEmpty()){
return "no data";
}
return hotels.toString();
}
}