1. 概述
Java REST Client 有两种风格:
(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仓库
2.2. 依赖
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
_
<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>
_
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.getTotalHits();
Page<BookModel> page = new Page<>(pageNo, pageSize, totalHits, list);
TimeValue took = searchResponse.getTook();
log.info("查询成功!请求参数: {}, 用时{}毫秒", searchRequest.source().toString(), took.millis());
return page;
} catch (IOException e) {
log.error("查询失败!原因: {}", e.getMessage(), e);
}
return null;
}
@Override
public void save(BookModel bookModel) {
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("id", bookModel.getId());
jsonMap.put("name", bookModel.getName());
jsonMap.put("author", bookModel.getAuthor());
jsonMap.put("category", bookModel.getCategory());
jsonMap.put("price", bookModel.getPrice());
jsonMap.put("sellTime", bookModel.getSellTime());
jsonMap.put("sellReason", bookModel.getSellReason());
jsonMap.put("status", bookModel.getStatus());
IndexRequest indexRequest = new IndexRequest(INDEX\_NAME, INDEX\_TYPE, String.valueOf(bookModel.getId()));
indexRequest.source(jsonMap);
client.indexAsync(indexRequest, RequestOptions.DEFAULT, new ActionListener<IndexResponse>() {
@Override
public void onResponse(IndexResponse indexResponse) {
String index = indexResponse.getIndex();
String type = indexResponse.getType();
String id = indexResponse.getId();
long version = indexResponse.getVersion();
log.info("Index: {}, Type: {}, Id: {}, Version: {}", index, type, id, version);
if (indexResponse.getResult() == DocWriteResponse.Result.CREATED) {
log.info("写入文档");
} else if (indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
log.info("修改文档");
}
ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
log.warn("部分分片写入成功");
}
if (shardInfo.getFailed() > 0) {
for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
String reason = failure.reason();
log.warn("失败原因: {}", reason);
}
}
}
@Override
public void onFailure(Exception e) {
log.error(e.getMessage(), e);
}
});
}
@Override
public void update(BookModel bookModel) {
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("sellReason", bookModel.getSellReason());
UpdateRequest request = new UpdateRequest(INDEX\_NAME, INDEX\_TYPE, String.valueOf(bookModel.getId()));
request.doc(jsonMap);
try {
UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("更新失败!原因: {}", e.getMessage(), e);
}
}
@Override
public void delete(int id) {
DeleteRequest request = new DeleteRequest(INDEX\_NAME, INDEX\_TYPE, String.valueOf(id));
try {
DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);
if (deleteResponse.status() == RestStatus.OK) {
log.info("删除成功!id: {}", id);
}
} catch (IOException e) {
log.error("删除失败!原因: {}", e.getMessage(), e);
}
}
@Override
public BookModel detail(int id) {
GetRequest getRequest = new GetRequest(INDEX\_NAME, INDEX\_TYPE, String.valueOf(id));
try {
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
if (getResponse.isExists()) {
String source = getResponse.getSourceAsString();
BookModel book = JSON.parseObject(source, BookModel.class);
return book;
}
} catch (IOException e) {
log.error("查看失败!原因: {}", e.getMessage(), e);
}
return null;
}
}
3.2.6. 页面
<link rel="stylesheet" href="/bootstrap-4/css/bootstrap.min.css">
<link rel="stylesheet" href="/bootstrap-table/bootstrap-table.css">
<script src="jquery-3.3.1.min.js"></script>
<script src="/bootstrap-4/js/bootstrap.min.js"></script>
<script src="/bootstrap-table/bootstrap-table.js"></script>
<script src="/bootstrap-table/locale/bootstrap-table-zh-CN.js"></script>
<script>
$(function(){
$('#table').bootstrapTable({
url: '/book/list',
method: 'get',
sidePagination: 'server',
responseHandler: function(res) { // 加载服务器数据之前的处理程序,可以用来格式化数据。参数:res为从服务器请求到的数据。
var result = {};
result.total = res.data.totalCount;
result.rows = res.data.pageList;
return result;
},
pagination: true,
pageSize: 3, // 初始PageSize
queryParams: function(params) {
var req = {
pageSize: params.limit,
pageNo: params.offset + 1
};
return req;
},
striped: true,
search: true,
columns: \[{
field: 'id',
title: 'ID'
}, {
field: 'name',
title: '名称'
}, {
field: 'author',
title: '作者'
}, {
field: 'price',
title: '单价'
}, {
field: 'sellTime',
title: '上架时间'
}, {
field: 'status',
title: '状态',
formatter: function(value) {
if (value == 1) {
return '<span style="color: green">可售</span>';
} else {
return '<span style="color: red">不可售</span>';
}
}
}, {
field: 'category',
title: '分类',
formatter: function(value) {
if (value == 10010) {
return '中国当代小说';
} else if (value == 10011) {
return '武侠小说';
} else if (value == 10012) {
return '爱情小说';
} else if (value == 10013) {
return '中国当代随笔';
}
}
}, {
field: 'sellReason',
title: '上架理由'
}, {
title: '操作',
formatter: function() {
return '<a href="#">修改</a> <a href="#">删除</a>';
}
}
\]
});
});
</script>
3.3. 演示
重点演示几个查询
返回结果:
{
"code": 200,
"success": true,
"msg": "SUCCESS",
"data": {
"pageNumber": 1,
"pageSize": 10,
"totalCount": 2,
"pageList": [
{
"id": 2,
"name": "倚天屠龙记(全四册)",
"author": "金庸",
"category": 10011,
"price": 70.4,
"sellReason": "武林至尊,宝刀屠龙,号令天下,莫敢不从。",
"sellTime": "2018-11-11",
"status": 1
},
{
"id": 3,
"name": "神雕侠侣",
"author": "金庸",
"category": 10011,
"price": 70,
"sellReason": "风陵渡口初相遇,一见杨过误终身",
"sellTime": "2018-11-11",
"status": 1
}
]
}
}
上面的查询对应的Elasticsearch DSL是这样的:
{
"from":0,
"size":10,
"query":{
"bool":{
"must":[
{
"match":{
"author":{
"query":"金庸",
"operator":"OR",
"prefix_length":0,
"max_expansions":50,
"fuzzy_transpositions":true,
"lenient":false,
"zero_terms_query":"NONE",
"auto_generate_synonyms_phrase_query":true,
"boost":1
}
}
},
{
"term":{
"status":{
"value":1,
"boost":1
}
}
},
{
"bool":{
"should":[
{
"term":{
"category":{
"value":10010,
"boost":1
}
}
},
{
"term":{
"category":{
"value":10011,
"boost":1
}
}
},
{
"term":{
"category":{
"value":10012,
"boost":1
}
}
}
],
"adjust_pure_negative":true,
"boost":1
}
}
],
"adjust_pure_negative":true,
"boost":1
}
},
"sort":[
{
"id":{
"order":"asc"
}
}
]
}
3.4. 工程结构
4. 参考
https://github.com/medcl/elasticsearch-analysis-ik
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-search.html
https://bootstrap-table.wenzhixin.net.cn/documentation/
5. 其它相关
本文摘自:https://www.cnblogs.com/cjsblog/p/10232581.html
感谢您的阅读,如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮。欢迎各位转载,但必须在文章页面中给出作者和原文连接。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章