<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.8.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.8.0</version>
</dependency>
void addIndex() throws Exception {
/**
* GET /user
* 作用:可以看到aliases、mappings、settings
* 说明:我们下面使用的是直接定死的结构,而SpringBoot中使用的是注解的方式来创建索引结构,最终的原理都是下面的代码
* {
* "user": {
* "aliases": {
* "user.aliases": {}
* },
* "mappings": {
* "properties": {
* "age": {
* "type": "integer"
* },
* "name": {
* "type": "text",
* "fields": {
* "keyword": {
* "type": "keyword"
* }
* }
* },
* "sex": {
* "type": "keyword"
* }
* }
* },
* "settings": {
* "index": {
* "creation_date": "1649243890532",
* "number_of_shards": "9",
* "number_of_replicas": "2",
* "uuid": "EPChtL_vQj2gHJbO5VTHqg",
* "version": {
* "created": "7060099"
* },
* "provided_name": "user"
* }
* }
* }
* }
*/
// 创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 定义索引名称
CreateIndexRequest request = new CreateIndexRequest("user");
// 添加aliases,对比上述结构来理解
String aliaseStr = "{\"user.aliases\":{}}";
Map aliases = JSONObject.parseObject(aliaseStr, Map.class);
// 添加mappings,对比上述结构来理解
String mappingStr = "{\"properties\":{\"name\":{\"type\":\"text\",\"fields\":{\"keyword\":{\"type\":\"keyword\"}}},\"sex\":{\"type\":\"keyword\"},\"age\":{\"type\":\"integer\"}}}";
Map mappings = JSONObject.parseObject(mappingStr, Map.class);
// 添加settings,对比上述结构来理解
String settingStr = "{\"index\":{\"number_of_shards\":\"9\",\"number_of_replicas\":\"2\"}}";
Map settings = JSONObject.parseObject(settingStr, Map.class);
// 添加数据
request.aliases(aliases);
request.mapping(mappings);
request.settings(settings);
// 发送请求到ES
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
// 处理响应结果
System.out.println("添加索引是否成功:" + response.isAcknowledged());
// 关闭ES客户端对象
client.close();
}
void getIndexInfo() throws Exception {
// 创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 定义索引名称
GetIndexRequest request = new GetIndexRequest("user");
// 发送请求到ES
GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);
// 处理响应结果
System.out.println("aliases:" + response.getAliases());
System.out.println("mappings:" + response.getMappings());
System.out.println("settings:" + response.getSettings());
// 关闭ES客户端对象
client.close();
}
void deleteIndex() throws Exception {
// 创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 定义索引名称
DeleteIndexRequest request = new DeleteIndexRequest("user");
// 发送请求到ES
AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
// 处理响应结果
System.out.println("删除是否成功:" + response.isAcknowledged());
// 关闭ES客户端对象
client.close();
}
void insertDataToIndex() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、创建请求对象
User user = new User();
user.setName("张三");
user.setSex("男");
user.setAge(22);
// 定义请求对象
IndexRequest request = new IndexRequest("user");
// 设置文档id
request.id("1000");
// 将json格式字符串放在请求中
request.source(JSONObject.toJSONString(user), XContentType.JSON);
// 3、发送请求到ES
IndexResponse response = client.index(request, RequestOptions.DEFAULT);
// 4、处理响应结果
System.out.println("数据插入结果:" + response.getResult());
// 5、关闭ES客户端对象
client.close();
}
void updateDataFromIndex() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
User user = new User();
user.setName("小美");
user.setAge(20);
UpdateRequest request = new UpdateRequest();
request.index("user").id("1000");
// 拓展:局部更新也可以这样写:request.doc(XContentType.JSON, "name", "李四", "age", 25);,其中"name"和"age"是User对象中的字段名称,而"小美"和20是对应的字段值
request.doc(JSONObject.toJSONString(user), XContentType.JSON);
// 3、发送请求到ES
UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
// 4、处理响应结果
System.out.println("数据更新结果:" + response.getResult());
// 5、关闭ES客户端对象
client.close();
}
void getDataById() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
GetRequest request = new GetRequest("user");
request.id("1000");
// 3、发送请求到ES
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 4、处理响应结果
System.out.println("查询结果:" + response.getSourceAsString());
// 5、关闭ES客户端对象
client.close();
}
void deleteDataById() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
DeleteRequest request = new DeleteRequest("user");
request.id("1000");
// 3、发送请求到ES
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
// 4、处理响应结果
System.out.println("删除是否成功:" + response.getResult());
// 5、关闭ES客户端对象
client.close();
}
void deleteDataByQuery() throws IOException {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
DeleteByQueryRequest request = new DeleteByQueryRequest("user");
request.setQuery(QueryBuilders.matchAllQuery());
// 3、发送请求到ES
BulkByScrollResponse response = client.deleteByQuery(request, RequestOptions.DEFAULT);
// 4、处理响应结果
System.out.println("删除失败结果:" + response.getBulkFailures());
// 5、关闭ES客户端对象
client.close();
}
void batchInsertDataToIndex() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
// 准备测试数据
List<User> userList = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
User user = new User();
user.setName(i % 2 == 0 ? "张三" + i : "小美" + i);
user.setSex(i % 2 == 0 ? "男" : "女");
user.setAge(22 + i);
userList.add(user);
}
BulkRequest bulkRequest = new BulkRequest();
// 准备批量插入的数据
userList.forEach(user -> {
// 设置请求对象
IndexRequest request = new IndexRequest("user");
// 文档id
request.id("10000" + user.getAge());
// 将json格式字符串放在请求中
// 下面这种写法也可以写成:request.source(XContentType.JSON, "name", "张三", "age", "男", "age", 22);,其中"name"、"age"、 "age"是User对象中的字段名,而这些字段名称后面的值就是对应的值
request.source(JSONObject.toJSONString(user), XContentType.JSON);
// 将request添加到批量处理请求中
bulkRequest.add(request);
});
// 3、发送请求到ES
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
// 4、处理响应结果
System.out.println("批量插入是否失败:" + response.hasFailures());
// 4.1、插入详细信息
for (BulkItemResponse itemResponse : response) {
BulkItemResponse.Failure failure = itemResponse.getFailure();
if (failure == null) {
System.out.println("插入成功的文档id:" + itemResponse.getId());
} else {
System.out.println("插入失败的文档id:" + itemResponse.getId());
}
}
// 5、关闭ES客户端对象
client.close();
}
void batchDeleteDataFromIndex() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
// 准备测试数据(只用到了age来生成文档id,但是为了和上面的批量插入应和,所以需要这样做)
List<User> userList = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
User user = new User();
user.setName(i % 2 == 0 ? "张三" + i : "小美" + i);
user.setSex(i % 2 == 0 ? "男" : "女");
user.setAge(22 + i);
userList.add(user);
}
BulkRequest bulkRequest = new BulkRequest();
// 准备批量插入的数据
userList.forEach(user -> {
// 设置请求对象
DeleteRequest request = new DeleteRequest("user");
// 文档id
request.id("10000" + user.getAge());
// 将request添加到批量处理请求中
bulkRequest.add(request);
});
// 3、发送请求到ES
BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);
// 4、处理响应结果
System.out.println("批量删除是否失败:" + response.hasFailures());
// 4.1、删除详细信息
for (BulkItemResponse itemResponse : response) {
BulkItemResponse.Failure failure = itemResponse.getFailure();
if (failure == null) {
System.out.println("删除成功的文档id:" + itemResponse.getId());
} else {
System.out.println("删除失败的文档id:" + itemResponse.getId());
}
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryFromAllData() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
// 用来查询索引中全部的数据
builder.query(QueryBuilders.matchAllQuery());
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
for (SearchHit hit : response.getHits().getHits()) {
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryByTerm() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
// 用来查询sex是男的数据
builder.query(QueryBuilders.termQuery("sex", "男"));
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
for (SearchHit hit : response.getHits().getHits()) {
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryByPage() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
// 分页查询数据,本次测试只查询前5条
builder.query(QueryBuilders.matchAllQuery());
int currentPage = 1;
int pageSize = 5;
int from = (currentPage - 1) * pageSize;
builder.from(from);
builder.size(pageSize);
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
for (SearchHit hit : response.getHits().getHits()) {
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryBySort() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchAllQuery());
// 根据年龄做降序排序
builder.sort("age", SortOrder.DESC);
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
for (SearchHit hit : response.getHits().getHits()) {
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryBySource() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.matchAllQuery());
// 如果查询的属性很少,那就使用includes,而excludes设置为空数组
// 如果排序的属性很少,那就使用excludes,而includes设置为空数组
String[] includes = {"name", "age"};
String[] excludes = {};
builder.fetchSource(includes, excludes);
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
for (SearchHit hit : response.getHits().getHits()) {
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryByShould() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.should(QueryBuilders.matchQuery("age", 30));
// 查询中boost默认是1,写成10可以增大score比分
boolQueryBuilder.should(QueryBuilders.matchQuery("sex", "女").boost(10));
builder.query(boolQueryBuilder);
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
for (SearchHit hit : response.getHits().getHits()) {
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryByFilter() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 查询年龄大于等于26,小于等于29的结果
boolQueryBuilder.filter(QueryBuilders.rangeQuery("age").gte(26).lte(29));
builder.query(boolQueryBuilder);
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
for (SearchHit hit : response.getHits().getHits()) {
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryByLike() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
// 和分词无关,这就是和mysql中like类似的做法
// 查询名称中包含“张三”的数据,或者比“张三”多一个字符的数据,这是通过Fuzziness.ONE来控制的,比如“张三1”是可以出现的,但是“张三12”是无法出现的,这是因为他比张三多了两个字符;除了“Fuzziness.ONE”之外,还可以是“Fuzziness.TWO”等
builder.query(QueryBuilders.fuzzyQuery("name.keyword", "张三").fuzziness(Fuzziness.ONE));
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
for (SearchHit hit : response.getHits().getHits()) {
User user = JSONObject.parseObject(hit.getSourceAsString(), User.class);
System.out.println("姓名:" + user.getName() + ";性别:" + user.getSex() + ";年龄:" + user.getAge());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryByHighLight() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
// 设置查询条件
builder.query(QueryBuilders.matchPhraseQuery("name", "张三"));
// 构建高亮查询对象
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 前置标签
highlightBuilder.preTags("");
// 后置标签
highlightBuilder.postTags("");
// 添加高亮的属性名称
highlightBuilder.field("name");
builder.highlighter(highlightBuilder);
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
SearchHit[] hits = response.getHits().getHits();
for (SearchHit hit : hits) {
Map<String, HighlightField> map = hit.getHighlightFields();
HighlightField highlightField = map.get("name");
System.out.println("高亮名称:" + highlightField.getFragments()[0].string());
}
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryByMaxValueAggregation() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
// 获取最大年龄
AggregationBuilder aggregationBuilder = AggregationBuilders.max("maxAge").field("age");
builder.aggregation(aggregationBuilder);
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
Aggregations aggregations = response.getAggregations();
ParsedMax maxAge = aggregations.get("maxAge");
System.out.println("最大年龄:" + maxAge.getValue());
// 5、关闭ES客户端对象
client.close();
}
void advancedQueryByGroupAggregation() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
SearchRequest request = new SearchRequest();
request.indices("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
// 按照性别分组,聚合操作要求和分词操作无关,由于sex在默认添加的时候是text类型,因为需要设置为keyword类型
AggregationBuilder aggregationBuilder = AggregationBuilders.terms("termsSex").field("sex.keyword");
builder.aggregation(aggregationBuilder);
request.source(builder);
// 3、发送请求到ES
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4、处理响应结果
Aggregations aggregations = response.getAggregations();
// 至于使用ParsedStringTerms、ParsedLongTerms、ParsedMax、ParsedNested、ParsedAvg……是由聚合要求和聚合字段类型确定的,比如本次要求是分组,并且聚合字段是sex,那就是String类型,所以使用ParsedStringTerms
ParsedStringTerms termsSex = aggregations.get("termsSex");
for (Terms.Bucket bucket : termsSex.getBuckets()) {
System.out.println("性别:" + bucket.getKeyAsString() + ";数量:" + bucket.getDocCount());
}
// 5、关闭ES客户端对象
client.close();
}
void count() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
CountRequest request = new CountRequest();
request.indices("user");
// 指定检索条件
request.query(QueryBuilders.matchAllQuery());
// 如果索引不存在,不会报错
request.indicesOptions(IndicesOptions.fromOptions(true, true, false, false));
// 3、发送请求到ES
CountResponse response = client.count(request, RequestOptions.DEFAULT);
// 4、处理响应结果
System.out.println("数据总量:" + response.getCount());
// 5、关闭ES客户端对象
client.close();
}
void scrollQuery() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
// 假设用户想获取第10页数据,其中每页20条
int totalPage = 10;
int size = 20;
SearchRequest searchRequest = new SearchRequest("user");
// 指定检索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
searchRequest.source(builder.query(QueryBuilders.matchAllQuery()).size(size));
String scrollId = null;
// 3、发送请求到ES
SearchResponse scrollResponce = null;
// 设置游标id存活时间
Scroll scroll = new Scroll(TimeValue.timeValueMinutes(2));
// 记录所有游标id
List<String> scrollIds = new ArrayList<>();
for (int i = 0; i < totalPage; i++) {
try {
// 首次检索
if (i == 0) {
//记录游标id
searchRequest.scroll(scroll);
// 首次查询需要指定索引名称和查询条件
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 下一次搜索要用到该游标id
scrollId = response.getScrollId();
// 记录所有游标id
scrollIds.add(scrollId);
}
// 非首次检索
else {
// 不需要在使用其他条件,也不需要指定索引名称,只需要使用执行游标id存活时间和上次游标id即可,毕竟信息都在上次游标id里面呢
SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
searchScrollRequest.scroll(scroll);
scrollResponce = client.scroll(searchScrollRequest, RequestOptions.DEFAULT);
// 下一次搜索要用到该游标id
scrollId = scrollResponce.getScrollId();
// 记录所有游标id
scrollIds.add(scrollId);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//清除游标id
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.scrollIds(scrollIds);
try {
client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
System.out.println("清除滚动查询游标id失败");
e.printStackTrace();
}
// 4、处理响应结果
System.out.println("滚动查询返回数据:" + scrollResponce);
// 5、关闭ES客户端对象
client.close();
}
void multiQueryById() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
MultiGetRequest request = new MultiGetRequest();
// user是索引名称,1000和2000都是文档id
request.add("user", "1000");
request.add("user", "2000");
// 3、发送请求到ES
MultiGetResponse response = client.mget(request, RequestOptions.DEFAULT);
// 4、处理响应结果
List<String> existIdList = new ArrayList<>();
for (MultiGetItemResponse itemResponse : response) {
MultiGetResponse.Failure failure = itemResponse.getFailure();
GetResponse getResponse = itemResponse.getResponse();
if (failure == null) {
boolean exists = getResponse.isExists();
String id = getResponse.getId();
if (exists) {
existIdList.add(id);
}
} else {
failure.getFailure().printStackTrace();
}
}
System.out.println("数据存在于ES的文档id:" + existIdList);
// 5、关闭ES客户端对象
client.close();
}
void printClusterNameAndStatus() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、定义请求对象
ClusterHealthRequest request = new ClusterHealthRequest();
// 3、发送请求到ES
ClusterHealthResponse response = client.cluster().health(request, RequestOptions.DEFAULT);
// 4、获取健康状况
ClusterHealthStatus status = response.getStatus();
// 5、打印集群名称
System.out.println("集群名称:" + response.getClusterName());
// 6、打印集群状态
System.out.println("集群健康状态:" + status.name());
// 7、关闭ES客户端对象
client.close();
}
void printIndexInfo() throws Exception {
// 1、创建ES客户端对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http")));
// 2、发送请求到ES
Response response = client.getLowLevelClient().performRequest(new Request("GET", "/_cat/indices"));
// 3、数据处理
HttpEntity entity = response.getEntity();
String responseStr = EntityUtils.toString(entity, StandardCharsets.UTF_8);
// 4、数据分解
String[] indexInfoArr = responseStr.split("\n");
for (String indexInfo : indexInfoArr) {
// 4.1、索引信息输出
String[] infoArr = indexInfo.split("\\s+");
String status = infoArr[0];
String open = infoArr[1];
String name = infoArr[2];
String id = infoArr[3];
String mainShardNum = infoArr[4];
String viceShardNum = infoArr[5];
String docNum = infoArr[6];
String deletedDocNum = infoArr[7];
String allShardSize = infoArr[8];
String mainShardSize = infoArr[9];
System.out.println("》》》》》》》》索引信息》》》》》》》》");
System.out.println("名称:" + name);
System.out.println("id:" + id);
System.out.println("状态:" + status);
System.out.println("是否开放:" + open);
System.out.println("主分片数量:" + mainShardNum);
System.out.println("副本分片数量:" + viceShardNum);
System.out.println("Lucene文档数量:" + docNum);
System.out.println("被删除文档数量:" + deletedDocNum);
System.out.println("所有分片大小:" + allShardSize);
System.out.println("主分片大小:" + mainShardSize);
}
// 6、关闭ES客户端对象
client.close();
}
// 方法1
CountRequest request = new CountRequest("index1", "index2");
// 方法2
CountRequest request = new CountRequest();
request.indices("index1", "index2");
// request可以是CountRequest、SearchRequest对象
// 注意:如果不设置下列参数,在查询的时候将会出现索引不存在错误
request.indicesOptions(IndicesOptions.fromOptions(true, true, false, false));
public class ESSaveServiceImpl implements ESSaveService {
@Resource
private RestHighLevelClient client;
@Override
public Boolean productUp(List<SkuEsModel> list) throws IOException {
// 创建批量处理请求
BulkRequest bulkRequest = new BulkRequest();
// 将需要处理的请求添加到批量处理请求中
list.forEach(model -> {
// 索引名称为product
IndexRequest request = new IndexRequest(ESConstant.PRODUCT_INDEX);
// 文档id,要求为String类型
// The id of the indexed document. If not set, will be automatically generated.
request.id(model.getSkuId().toString());
// 将需要传输的数据转换为json格式字符串
String jsonString = JSONObject.toJSONString(model);
// 将json格式字符串放在请求中
request.source(jsonString, XContentType.JSON);
// 将request添加到批量处理请求中
bulkRequest.add(request);
});
// 发送请求到es进行批量处理
BulkResponse bulk = client.bulk(bulkRequest, ElasticsearchConfig.COMMON_OPTIONS);
// 返回批量处理是否成功
return bulk.hasFailures();
}
}
PUT gulimall_product
{
"mappings": {
"properties": {
"skuId": {
"type": "long"
},
"spuId": {
"type": "keyword"
},
"skuTitle": {
"type": "text",
"analyzer": "ik_smart"
},
"skuPrice": {
"type": "double"
},
"skuImg": {
"type": "keyword"
},
"saleCount": {
"type": "long"
},
"hasStock": {
"type": "boolean"
},
"hotScore": {
"type": "long"
},
"brandId": {
"type": "long"
},
"catalogId": {
"type": "long"
},
"brandName": {
"type": "keyword"
},
"brandImg": {
"type": "keyword"
},
"catalogName": {
"type": "keyword"
},
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrName": {
"type": "keyword"
},
"attrValue": {
"type": "keyword"
}
}
}
}
}
}
public class ESSearchServiceImpl implements ESSearchService {
@Resource
private RestHighLevelClient client;
@Resource
private ProductFeignService productFeignService;
@Override
public SearchResult search(SearchParam param) {
// 返回数据
SearchResult result = new SearchResult();
// 1、创建检索请求
SearchRequest searchRequest = buildSearchRequest(param);
try {
// 2、执行检索,获取响应数据
SearchResponse response = client.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS);
// 3、分析响应数据,将响应数据封装成我们想要的格式
result = buildSearchResult(response, param);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 构建检索请求
*
* @return
*/
private SearchRequest buildSearchRequest(SearchParam param) {
// 指定Query DSL,即检索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// **************************构建查询条件start***********************
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
if (StringUtils.isNotEmpty(param.getKeyword())) {
boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", param.getKeyword()));
}
if (ObjectUtils.allNotNull(param.getCatalog3Id())) {
boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", param.getCatalog3Id()));
}
if (ObjectUtils.allNotNull(param.getBrandId()) && param.getBrandId().size() > 0) {
boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
}
if (ObjectUtils.allNotNull(param.getAttrs()) && param.getAttrs().size() > 0) {
param.getAttrs().forEach(attr -> {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// attrs=1_安卓:苹果:塞班&attrs=2_5寸:6寸
String[] s1 = attr.split("_");
String[] s2 = s1[1].split(":");
boolQuery.must(QueryBuilders.termQuery("attrs.attrId", s1[0]));
boolQuery.must(QueryBuilders.termsQuery("attrs.attrValue", s2));
// ScoreMode.None表示不参与评分
boolQueryBuilder.filter(QueryBuilders.nestedQuery("attrs", boolQuery, ScoreMode.None));
});
}
if (ObjectUtils.allNotNull(param.getHasStock())) {
boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
}
if (StringUtils.isNotEmpty(param.getSkuPrice())) {
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
String[] s = param.getSkuPrice().split("_");
if (s.length == 2 && StringUtils.isNotEmpty(s[0])) {
rangeQuery.gte(s[0]).lte(s[1]);
} else if (s.length == 2 && param.getSkuPrice().startsWith("_")) {
// 由于"_8000"分割之后会出现["", "8000"]这种情况,所以我们这样做
rangeQuery.lte(s[1]);
} else if (s.length == 1 && param.getSkuPrice().endsWith("_")) {
rangeQuery.gte(s[0]);
}
boolQueryBuilder.filter(rangeQuery);
}
sourceBuilder.query(boolQueryBuilder);
// **************************构建查询条件end***********************
// **************************构建排序条件start***********************
/*
* 排序条件
* sort=saleCount_asc/desc---》销量
* sort=skuPrice_asc/desc---》价格
* sort=hotScore_asc/desc---》热度评分
*/
if (StringUtils.isNotEmpty(param.getSort())) {
String[] s = param.getSort().split("_");
SortOrder order = "asc".equalsIgnoreCase(s[1]) ? SortOrder.ASC : SortOrder.DESC;
sourceBuilder.sort(s[0], order);
}
// **************************构建排序条件end***********************
// **************************构建分页条件start***********************
int start = (param.getPageNum() - 1) * ESConstant.PAGE_SIZE;
sourceBuilder.from(start).size(ESConstant.PAGE_SIZE);
// **************************构建分页条件end***********************
// **************************构建高亮条件start***********************
if (StringUtils.isNotEmpty(param.getKeyword())) {
HighlightBuilder builder = new HighlightBuilder();
builder.preTags("");
builder.postTags("");
builder.field("skuTitle");
sourceBuilder.highlighter(builder);
}
// **************************构建高亮条件end***********************
// **************************构建聚合条件start***********************
// 创建聚合
TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brand_agg")
.field("brandId").size(32);
// 添加子聚合
brandAgg.subAggregation(AggregationBuilders.terms("brand_name_agg")
.field("brandName").size(1));
brandAgg.subAggregation(AggregationBuilders.terms("brand_img_agg")
.field("brandImg").size(1));
// 创建聚合
TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalog_agg")
.field("catalogId").size(14);
// 添加子聚合
catalogAgg.subAggregation(AggregationBuilders.terms("catalog_name_agg")
.field("catalogName").size(1));
// 创建聚合
NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attr_agg", "attrs");
// 添加子聚合
TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attr_id_agg")
.field("attrs.attrId").size(14);
// 添加孙子聚合
attrIdAgg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));
attrIdAgg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(12));
attrAgg.subAggregation(attrIdAgg);
// 将聚合添加到请求中
sourceBuilder.aggregation(brandAgg);
sourceBuilder.aggregation(catalogAgg);
sourceBuilder.aggregation(attrAgg);
// **************************构建聚合条件end***********************
// 创建检索请求:首先要指定被检索的索引,然后添加检索条件
SearchRequest searchRequest = new SearchRequest(new String[]{ESConstant.PRODUCT_INDEX}, sourceBuilder);
return searchRequest;
}
/**
* 构建响应结果
*
* @return
*/
private SearchResult buildSearchResult(SearchResponse response, SearchParam param) {
// 总返回结果
SearchResult result = new SearchResult();
// 获取es返回的结果
SearchHits hits = response.getHits();
// 1、所有返回的商品信息
List<SkuEsModel> products = new ArrayList<>();
// 获得检索信息
SearchHit[] searchHits = hits.getHits();
if (searchHits != null && searchHits.length > 0) {
// 将hits中的hits中的值放在对象中
for (SearchHit hit : searchHits) {
String str = hit.getSourceAsString();
// 普通类型可以直接这样写,复杂类型需要用new TypeReference<类型>(){}来完成,例如List就是复杂类型
SkuEsModel skuEsModel = JSONObject.parseObject(str, SkuEsModel.class);
// 如果有全文检索关键字,那就需要使用高亮显示
if (StringUtils.isNotEmpty(param.getKeyword())) {
// 以下结果是debug返回值response,分析控制台中的response中的internalResponse---》hits---》hits得出来的
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField highlightField = highlightFields.get("skuTitle");
String skuTitle = highlightField.getFragments()[0].string();
skuEsModel.setSkuTitle(skuTitle);
}
products.add(skuEsModel);
}
}
// 将商品信息放在总返回结果中
result.setProducts(products);
// 获得聚合信息
// 注意:聚合中用到的ParsedLongTerms、ParsedStringTerms、ParsedNested类型来自于debug返回值response,通过查看控制台response中的internalResponse---》aggregations得出返回值类型
Aggregations aggregations = response.getAggregations();
// 2、检索出来的所有品牌信息
List<SearchResult.BrandVo> brands = new ArrayList<>();
// 分析聚合信息
ParsedLongTerms brandAgg = aggregations.get("brand_agg");
// 判断聚合信息是否为空
if (ObjectUtils.allNotNull(brandAgg) && brandAgg.getBuckets().size() > 0) {
for (Terms.Bucket bucket : brandAgg.getBuckets()) {
// 返回对象
SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
// 放品牌id
String brandId = bucket.getKeyAsString();
brandVo.setBrandId(Long.parseLong(brandId));
// 放品牌图片
ParsedStringTerms brandImgAgg = bucket.getAggregations().get("brand_img_agg");
brandVo.setBrandImg(brandImgAgg.getBuckets().get(0).getKeyAsString());
// 放品牌名称
ParsedStringTerms brandNameAgg = bucket.getAggregations().get("brand_name_agg");
brandVo.setBrandName(brandNameAgg.getBuckets().get(0).getKeyAsString());
// 将对象放入返回值
brands.add(brandVo);
}
}
// 将聚合信息添加到总返回结果中
result.setBrands(brands);
// 3、检索出来的所有分类信息
List<SearchResult.CatalogVo> catalogs = new ArrayList<>();
// 分析聚合信息
ParsedLongTerms catalogAgg = aggregations.get("catalog_agg");
// 判断聚合信息是否为空
if (ObjectUtils.allNotNull(catalogAgg) && catalogAgg.getBuckets().size() > 0) {
for (Terms.Bucket bucket : catalogAgg.getBuckets()) {
// 返回对象
SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
// 放分类id
String catalogId = bucket.getKeyAsString();
catalogVo.setCatalogId(Long.parseLong(catalogId));
// 放分类名称
ParsedStringTerms catalogNameAgg = bucket.getAggregations().get("catalog_name_agg");
catalogVo.setCatalogName(catalogNameAgg.getBuckets().get(0).getKeyAsString());
// 将对象放入返回值
catalogs.add(catalogVo);
}
}
// 将聚合信息添加到总返回结果中
result.setCatalogs(catalogs);
// 4、所有涉及到的可以检索的属性信息
List<SearchResult.AttrVo> attrs = new ArrayList<>();
// 分析聚合信息
ParsedNested attrAgg = aggregations.get("attr_agg");
ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attr_id_agg");
// 判断聚合信息是否为空
if (ObjectUtils.allNotNull(attrIdAgg) && attrIdAgg.getBuckets().size() > 0) {
for (Terms.Bucket bucket : attrIdAgg.getBuckets()) {
// 返回对象
SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
// 放分类id
String attrId = bucket.getKeyAsString();
attrVo.setAttrId(Long.parseLong(attrId));
// 放分类名称
ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attr_name_agg");
attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
// 放分类值
ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attr_value_agg");
// 创建分类值列表
List<String> attrValue = new ArrayList<>();
for (Terms.Bucket bucket1 : attrValueAgg.getBuckets()) {
attrValue.add(bucket1.getKeyAsString());
}
// 放分类值列表
attrVo.setAttrValue(attrValue);
// 将对象放入返回值
attrs.add(attrVo);
}
}
// 将聚合信息添加到总返回结果中
result.setAttrs(attrs);
// 5、总记录数
long total = hits.getTotalHits().value;
result.setTotal(total);
// 6、总页数
int totalPage = (int) total % ESConstant.PAGE_SIZE > 0 ? ((int) total / ESConstant.PAGE_SIZE + 1) : (int) total / ESConstant.PAGE_SIZE;
result.setTotalPage(totalPage);
// 7、当前页
result.setPageNum(param.getPageNum());
// 8、面包屑导航
// 处理属性面包屑导航
if (param.getAttrs() != null && param.getAttrs().size() > 0) {
List<SearchResult.NavVo> navVos = param.getAttrs().stream().map(attr -> {
SearchResult.NavVo navVo = new SearchResult.NavVo();
// attr就类似于attrs=1_安卓:苹果:塞班中的1_安卓:苹果:塞班
String[] s = attr.split("_");
// 将所有属性id依次放在属性id集合中,前端隐藏属性选择表的时候可以用
result.getAttrIds().add(Long.parseLong(s[0]));
// 把navValue按照v1,v2……这种方式放在navVo返回值中
String[] values = s[1].split(":");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
sb.append(values[i]);
if (i < values.length - 1) {
sb.append(",");
}
}
navVo.setNavValue(sb.toString());
// 把navName放在navVo返回值中
R r = productFeignService.attrInfo(Long.parseLong(s[0]));
if (r.getCode() == Constant.SUCCESS_CODE) {
AttrRespVo attrRespVo = r.getData("attr", new TypeReference<AttrRespVo>() {
});
navVo.setNavName(attrRespVo.getAttrName());
} else {
navVo.setNavName(s[0]);
}
// 把link放在navVo返回值中
String link = getLinkByAttrs(param.get_requestURI(), param.get_queryString(), "attrs", attr);
navVo.setLink(link);
// 返回相应对象
return navVo;
}).collect(Collectors.toList());
// 将面包屑导航集合放在返回结果中
result.setNavVos(navVos);
}
// 处理商标面包屑导航
if (param.getBrandId() != null && param.getBrandId().size() > 0) {
// 获取面包屑导航集合
List<SearchResult.NavVo> navVos = result.getNavVos();
SearchResult.NavVo navVo = new SearchResult.NavVo();
navVo.setNavName("品牌");
// 从远程获取数据
R r = productFeignService.brandInfo(param.getBrandId());
// 判断获取远程数据是否成功
if (r.getCode() == Constant.SUCCESS_CODE) {
// 获取远程数据
List<BrandVo> brandVos = r.getData("brand", new TypeReference<List<BrandVo>>() {
});
// 用来存储商标名称,毕竟在设计之初可以多选,最后名称类似于"华为,Apple"
StringBuilder sb = new StringBuilder();
// 去掉所有商标信息的地址
String link = null;
// 所有商标id集合
List<Long> brandIds = new ArrayList<>();
for (int i = 0; i < brandVos.size(); i++) {
sb.append(brandVos.get(i).getName());
if (i < brandVos.size() - 1) {
sb.append(",");
}
brandIds.add(brandVos.get(i).getBrandId());
}
link = getLinkByBrandId(param.get_requestURI(), param.get_queryString(), "brandId", brandIds);
// 将经过遍历的值添加进去
navVo.setLink(link);
navVo.setNavValue(sb.toString());
} else {
// 获取远程数据出错,进行处理
navVo.setNavValue("首页");
navVo.setLink("http://gulimall.com");
}
// 将商标信息添加到面包屑导航中
navVos.add(navVo);
}
// 最终返回给前端的数据
return result;
}
// 删除相关属性,然后获取链接
private String getLinkByAttrs(String requestUri, String queryString, String key, String value) {
String encode = null;
try {
// 前端地址会把空格变成%20,而后端使用URLEncoder.encode()之后会把空格变成+,所以我们需要使用replaceAll替换操作,还有执行URLEncoder.encode()之后会将/转义,但是我不需要转义
// 注意:只能改变自己去适应前端变化,不能改变前端的地址信息,否则前端不能访问了怎么办?
encode = URLEncoder.encode(value, "UTF-8").replaceAll("\\+", "%20").replaceAll("%2F","/");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 可能该属性是中间值或者最后一个值(前面还有值)
queryString = queryString.replace("&" + key + "=" + encode, "");
// 可能该属性是第一个值,后面还有值
queryString = queryString.replace(key + "=" + encode + "&", "");
// 可能该属性是第一个值,后面没有值
queryString = queryString.replace(key + "=" + encode, "");
if (StringUtils.isNotEmpty(queryString)) {
return "http://search.gulimall.com" + requestUri + "?" + queryString;
} else {
return "http://search.gulimall.com" + requestUri;
}
}
// 删除所有品牌id,然后获取链接
private String getLinkByBrandId(String requestUri, String queryString, String key, List<Long> values) {
// 把所有品牌id参数都去掉
for (Long value : values) {
// 可能该属性是中间值或者最后一个值(前面还有值)
queryString = queryString.replace("&" + key + "=" + value, "");
// 可能该属性是第一个值,后面还有值
queryString = queryString.replace(key + "=" + value + "&", "");
// 可能该属性是第一个值,后面没有值
queryString = queryString.replace(key + "=" + value, "");
}
if (StringUtils.isNotEmpty(queryString)) {
return "http://search.gulimall.com" + requestUri + "?" + queryString;
} else {
return "http://search.gulimall.com" + requestUri;
}
}
}
请看 四、实战》1、批量插入》1.1、对应索引结构