Elasticsearch Java高级客户端
Posted 狂乱的贵公子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Elasticsearch Java高级客户端相关的知识,希望对你有一定的参考价值。
1. 概述
Java REST Client 有两种风格:
- Java Low Level REST Client :用于Elasticsearch的官方低级客户端。它允许通过http与Elasticsearch集群通信。将请求编排和响应反编排留给用户自己处理。它兼容所有的Elasticsearch版本。(PS:学过WebService的话,对编排与反编排这个概念应该不陌生。可以理解为对请求参数的封装,以及对响应结果的解析)
- Java High Level REST Client :用于Elasticsearch的官方高级客户端。它是基于低级客户端的,它提供很多API,并负责请求的编排与响应的反编排。(PS:就好比是,一个是传自己拼接好的字符串,并且自己解析返回的结果;而另一个是传对象,返回的结果也已经封装好了,直接是对象,更加规范了参数的名称以及格式,更加面对对象一点)
(PS:所谓低级与高级,我觉得一个很形象的比喻是,面向过程编程与面向对象编程)
在 Elasticsearch 7.0 中不建议使用TransportClient,并且在8.0中会完全删除TransportClient。因此,官方更建议我们用Java High Level REST Client,它执行HTTP请求,而不是序列号的Java请求。既然如此,这里就直接用高级了。
2. Java High Level REST Client (高级REST客户端)
2.1. Maven仓库
<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>6.5.4</version> </dependency>
2.2. 依赖
- org.elasticsearch.client:elasticsearch-rest-client
- org.elasticsearch:elasticsearch
2.3. 初始化
RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http"), new HttpHost("localhost", 9201, "http")));
高级客户端内部会创建低级客户端用于基于提供的builder执行请求。低级客户端维护一个连接池,并启动一些线程,因此当你用完以后应该关闭高级客户端,并且在内部它将会关闭低级客户端,以释放这些资源。关闭客户端可以使用close()方法:
client.close();
2.4. 文档API
2.4.1. 添加文档
IndexRequest
IndexRequest request = new IndexRequest("posts", "doc", "1"); String jsonString = "{\\"user\\":\\"kimchy\\",\\"postDate\\":\\"2013-01-30\\",\\"message\\":\\"trying out Elasticsearch\\"}"; request.source(jsonString, XContentType.JSON);
提供文档source的方式还有很多,比如:
通过Map的方式提供文档source
通过XContentBuilder方式提供source
通过Object的方式(键值对)提供source
可选参数
同步执行
异步执行
你也可以异步执行 IndexRequest,为此你需要指定一个监听器来处理这个异步响应结果:
一个典型的监听器看起来是这样的:
IndexResponse
如果有版本冲突,将会抛出ElasticsearchException
同样的异常也有可能发生在当opType设置为create的时候,且相同索引、相同类型、相同ID的文档已经存在时。例如:
2.4.2. 查看文档
Get Request
可选参数
同步执行
异步执行
Get Response
当索引不存在,或者指定的文档的版本不存在时,响应状态吗是404,并且抛出ElasticsearchException
2.4.3. 文档是否存在
2.4.4. 删除文档
Delete Request
可选参数
同添加
2.5. 搜索API
Search Request
基本格式是这样的:
大多数查询参数被添加到 SearchSourceBuilder
可选参数
SearchSourceBuilder
控制检索行为的大部分选项都可以在SearchSourceBuilder中设置。下面是一个常见选项的例子:
在这个例子中,我们首先创建了一个SearchSourceBuilder对象,并且带着默认选项。然后设置了一个term查询,接着设置检索的位置和数量,最后设置超时时间
在设置完这些选项以后,我们只需要把SearchSourceBuilder加入到SearchRequest中即可
构建Query
用QueryBuilder来创建Serarch Query。QueryBuilder支持Elasticsearch DSL中每一种Query
例如:
还可以通过QueryBuilders工具类来创建QueryBuilder对象,例如:
无论是用哪种方式创建,最后一定要把QueryBuilder添加到SearchSourceBuilder中
排序
SearchSourceBuilder 可以添加一个或多个 SortBuilder
SortBuilder有四种实现:FieldSortBuilder、GeoDistanceSortBuilder、ScoreSortBuilder、ScriptSortBuilder
聚集函数
同步执行
异步执行
从查询响应中取出文档
3. 示例
3.1. 准备数据
3.1.1. 安装IK分词器插件
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.5.4/elasticsearch-analysis-ik-6.5.4.zip
3.1.2. 创建索引
curl -X PUT "localhost:9200/book" -H \'Content-Type: application/json\' -d\' { "mappings":{ "_doc":{ "properties":{ "id":{ "type":"integer" }, "name":{ "type":"text", "analyzer":"ik_max_word", "search_analyzer":"ik_max_word" }, "author":{ "type":"text", "analyzer":"ik_max_word", "search_analyzer":"ik_max_word" }, "category":{ "type":"integer" }, "price":{ "type":"double" }, "status":{ "type":"short" }, "sellReason":{ "type":"text", "analyzer":"ik_max_word", "search_analyzer":"ik_max_word" }, "sellTime":{ "type":"date", "format":"yyyy-MM-dd" } } } } } \'
3.1.3. 数据预览
3.2. 示例代码
3.2.1. 完整的pom.xml
<?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 http://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.1.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.cjs.example</groupId> <artifactId>elasticsearch-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>elasticsearch-demo</name> <description></description> <properties> <java.version>1.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-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>6.5.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.8</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.54</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</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> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3.2.2. 配置
package com.cjs.example.elasticsearch.config; import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestHighLevelClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author ChengJianSheng * @date 2019-01-07 */ @Configuration public class ElasticsearchClientConfig { @Bean public RestHighLevelClient restHighLevelClient() { RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http"))); return client; } }
3.2.3. domain
package com.cjs.example.elasticsearch.domain.model; import lombok.Data; import java.io.Serializable; /** * 图书 * @author ChengJianSheng * @date 2019-01-07 */ @Data public class BookModel implements Serializable { private Integer id; // 图书ID private String name; // 图书名称 private String author; // 作者 private Integer category; // 图书分类 private Double price; // 图书价格 private String sellReason; // 上架理由 private String sellTime; // 上架时间 private Integer status; // 状态(1:可售,0:不可售) }
3.2.4. Controller
package com.cjs.example.elasticsearch.controller; import com.alibaba.fastjson.JSON; import com.cjs.example.elasticsearch.domain.common.BaseResult; import com.cjs.example.elasticsearch.domain.common.Page; import com.cjs.example.elasticsearch.domain.model.BookModel; import com.cjs.example.elasticsearch.domain.vo.BookRequestVO; import com.cjs.example.elasticsearch.service.BookService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; /** * 文档操作 * @author ChengJianSheng * @date 2019-01-07 */ @Slf4j @RestController @RequestMapping("/book") public class BookController { @Autowired private BookService bookService; /** * 列表分页查询 */ @GetMapping("/list") public BaseResult list(BookRequestVO bookRequestVO) { Page<BookModel> page = bookService.list(bookRequestVO); if (null == page) { return BaseResult.error(); } return BaseResult.ok(page); } /** * 查看文档 */ @GetMapping("/detail") public BaseResult detail(Integer id) { if (null == id) { return BaseResult.error("ID不能为空"); } BookModel book = bookService.detail(id); return BaseResult.ok(book); } /** * 添加文档 */ @PostMapping("/add") public BaseResult add(@RequestBody BookModel bookModel) { bookService.save(bookModel); log.info("插入文档成功!请求参数: {}", JSON.toJSONString(bookModel)); return BaseResult.ok(); } /** * 修改文档 */ @PostMapping("/update") public BaseResult update(@RequestBody BookModel bookModel) { Integer id = bookModel.getId(); if (null == id) { return BaseResult.error("ID不能为空"); } BookModel book = bookService.detail(id); if (null == book) { return BaseResult.error("记录不存在"); } bookService.update(bookModel); log.info("更新文档成功!请求参数: {}", JSON.toJSONString(bookModel)); return BaseResult.ok(); } /** * 删除文档 */ @GetMapping("/delete") public BaseResult delete(Integer id) { if (null == id) { return BaseResult.error("ID不能为空"); } bookService.delete(id); return BaseResult.ok(); } }
3.2.5. Service
package com.cjs.example.elasticsearch.service.impl; import com.alibaba.fastjson.JSON; import com.cjs.example.elasticsearch.domain.common.Page; import com.cjs.example.elasticsearch.domain.model.BookModel; import com.cjs.example.elasticsearch.domain.vo.BookRequestVO; import com.cjs.example.elasticsearch.service.BookService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DocWriteResponse; 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.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.replication.ReplicationResponse; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.query.BoolQueryBuilder; 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.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; /** * @author ChengJianSheng * @date 2019-01-07 */ @Slf4j @Service public class BookServiceImpl implements BookService { private static final String INDEX_NAME = "book"; private static final String INDEX_TYPE = "_doc"; @Autowired private RestHighLevelClient client; @Override public Page<BookModel> list(BookRequestVO bookRequestVO) { int pageNo = bookRequestVO.getPageNo(); int pageSize = bookRequestVO.getPageSize(); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); sourceBuilder.from(pageNo - 1); sourceBuilder.size(pageSize); sourceBuilder.sort(new FieldSortBuilder("id").order(SortOrder.ASC)); // sourceBuilder.query(QueryBuilders.matchAllQuery()); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); if (StringUtils.isNotBlank(bookRequestVO.getName())) { boolQueryBuilder.must(QueryBuilders.matchQuery("name", bookRequestVO.getName())); } if (StringUtils.isNotBlank(bookRequestVO.getAuthor())) { boolQueryBuilder.must(QueryBuilders.matchQuery("author", bookRequestVO.getAuthor())); } if (null != bookRequestVO.getStatus()) { boolQueryBuilder.must(QueryBuilders.termQuery("status", bookRequestVO.getStatus())); } if (StringUtils.isNotBlank(bookRequestVO.getSellTime())) { boolQueryBuilder.must(QueryBuilders.termQuery("sellTime", bookRequestVO.getSellTime())); } if (StringUtils.isNotBlank(bookRequestVO.getCategories())) { String[] categoryArr = bookRequestVO.getCategories().split(","); List<Integer> categoryList = Arrays.asList(categoryArr).stream().map(e->Integer.valueOf(e)).collect(Collectors.toList()); BoolQueryBuilder categoryBoolQueryBuilder = QueryBuilders.boolQuery(); for (Integer category : categoryList) { categoryBoolQueryBuilder.should(QueryBuilders.termQuery("category", category)); } boolQueryBuilder.must(categoryBoolQueryBuilder); } sourceBuilder.query(boolQueryBuilder); SearchRequest searchRequest = new SearchRequest(); searchRequest.indices(INDEX_NAME); searchRequest.source(sourceBuilder); try { SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); RestStatus restStatus = searchResponse.status(); if (restStatus != RestStatus.OK) { return null; } List<BookModel> list = new ArrayList<>(); SearchHits searchHits = searchResponse.getHits(); for (SearchHit hit : searchHits.getHits()) { String source = hit.getSourceAsString(); BookModel book = JSON.parseObject(source, BookModel.class); list.add(book); } long totalHits = searchHits.getTotalH以上是关于Elasticsearch Java高级客户端的主要内容,如果未能解决你的问题,请参考以下文章
Elasticsearch Java高级REST客户端建立一堆TCP连接,并且在放入数据后不要关闭该连接
来聊一聊 ElasticSearch 最新版的 Java 客户端