Springboot2.5.6整合Elasticsearch7.12.1完整示例
SpringBoot是2.5.6
,elasticsearch是7.12.1
使用依赖elasticsearch-rest-high-level-client
使用RestHighLevelClient
操作
1、引入Pom文件的依赖
<?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.5.6</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-boot-elasticsearch4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-elasticsearch4</name>
<description>spring-boot-elasticsearch4</description>
<properties>
<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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、添加配置文件ElasticsearchConfig
package com.example.springbootelasticsearch4.config;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
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.ArrayList;
/**
* @author zhangshixing
* @date 2021年11月11日 13:55
* elasticsearch 配置类
*/
@Configuration
public class ElasticsearchConfig {
@Value("${elasticsearch.address}")
private String address;
/**
* 连接超时时间
*/
@Value("${elasticsearch.connect-timeout}")
private int connectTimeOut = 1000;
/**
* 连接超时时间
*/
@Value("${elasticsearch.socket-timeout}")
private int socketTimeOut = 30000;
/**
* 获取连接的超时时间
*/
@Value("${elasticsearch.connection-request-timeout}")
private int connectionRequestTimeOut = 500;
/**
* 最大连接数
*/
@Value("${elasticsearch.max-connect-num}")
private int maxConnectNum = 100;
/**
* 最大路由连接数
*/
@Value("${elasticsearch.max-connect-per-route}")
private int maxConnectPerRoute = 100;
@Bean
RestHighLevelClient restHighLevelClient() {
System.out.println("elasticsearch init......");
ArrayList<HttpHost> hostList = new ArrayList<>();
String[] addrss = address.split(",");
for (String addr : addrss) {
String[] arr = addr.split(":");
hostList.add(new HttpHost(arr[0], Integer.parseInt(arr[1]), "http"));
}
RestClientBuilder builder = RestClient.builder(hostList.toArray(new HttpHost[0]));
// 异步httpclient连接延时配置
builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
@Override
public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
requestConfigBuilder.setConnectTimeout(connectTimeOut);
requestConfigBuilder.setSocketTimeout(socketTimeOut);
requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeOut);
return requestConfigBuilder;
}
});
// 异步httpclient连接数配置
builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
httpClientBuilder.setMaxConnTotal(maxConnectNum);
httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
return httpClientBuilder;
}
});
RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
}
3、配置yml文件
# elasticsearch配置
# 如果是集群,用逗号隔开
elasticsearch.address = 127.0.0.1:9200
# 连接超时时间
elasticsearch.connect-timeout = 1000
# 连接超时时间
elasticsearch.socket-timeout = 30000
elasticsearch.connection-request-timeout = 500
elasticsearch.max-connect-num = 100
elasticsearch.max-connect-per-route = 100
4、自定义注解创建索引
这里简单说一下,索引就相当于是表结构,es本身也是存储数据的,既然是存储,就需要定一个结构,比如有哪
些字段,每个字段是什么类型。但是痛点是如果我们定义的这个结构如果比较复杂,那么用原生的方法代码会很
多,很麻烦,所以我们可以自己定义一套注解,加入到实体类上,这样就可以根据实体类,让es自己去创建索
引,很方便。就类似于以前hibernate,可以根据我们写的实体类自动生表。
关于注解,这里也给出以下,在之前文章基础上做了些改动,主要就是加入了EsId注解,可以将制定字段作为es的
id,如果不加这个,es默认id是自动生成的,有了这个,那么我们可以让mysql的id直接作为es的id,方便更新。
关于这块的内容参见:https://blog.csdn.net/lsqingfeng/article/details/106526493
package com.example.springbootelasticsearch4.annotation;
import java.lang.annotation.*;
/**
* @author zhangshixing
* @date 2021年11月11日 16:01
* Es 文档注解,用于做索引实体映射
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
public @interface Document {
/**
* index : 索引名称
*
* @return
*/
String index();
/**
* 类型名称
*
* @return
*/
String type();
}
package com.example.springbootelasticsearch4.annotation;
import java.lang.annotation.*;
/**
* @author zhangshixing
* @date 2021年11月11日 16:02
* 用于标识使用,该字段作为ES数据中的id
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface EsId {
}
package com.example.springbootelasticsearch4.annotation;
import java.lang.annotation.*;
/**
* @author zhangshixing
* @date 2021年11月11日 16:03
* 作用在字段上,用于定义类型,映射关系
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
public @interface Field {
FieldType type() default FieldType.TEXT;
/**
* 指定分词器
*
* @return
*/
AnalyzerType analyzer() default AnalyzerType.STANDARD;
}
两个枚举:
package com.example.springbootelasticsearch4.annotation;
/**
* @author zhangshixing
* @date 2021年11月11日 19:05
*/
import lombok.Getter;
/**
* es 类型参看
* https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html
*/
@Getter
public enum FieldType {
/**
* text
*/
TEXT("text"),
KEYWORD("keyword"),
INTEGER("integer"),
DOUBLE("double"),
DATE("date"),
/**
* 单条数据
*/
OBJECT("object"),
/**
* 嵌套数组
*/
NESTED("nested"),;
FieldType(String type) {
this.type = type;
}
private String type;
}
package com.example.springbootelasticsearch4.annotation;
import lombok.Getter;
/**
* @author zhangshixing
* @date 2021年11月11日 16:04
*/
@Getter
public enum AnalyzerType {
NO("不使用分词"),
/**
* 标准分词,默认分词器
*/
STANDARD("standard"),
/**
* ik_smart:会做最粗粒度的拆分;已被分出的词语将不会再次被其它词语占有
*/
IK_SMART("ik_smart"),
/**
* ik_max_word :会将文本做最细粒度的拆分;尽可能多的拆分出词语
*/
IK_MAX_WORD("ik_max_word");
private String type;
AnalyzerType(String type) {
this.type = type;
}
}
5、封装Elasticsearch工具类
package com.example.springbootelasticsearch4.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.springbootelasticsearch4.annotation.Document;
import com.example.springbootelasticsearch4.annotation.EsId;
import com.example.springbootelasticsearch4.annotation.FieldType;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
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.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
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.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
/**
* @author zhangshixing
* @date 2021年11月11日 19:10
* es 操作工具类,这里均采用同步调用的方式
*/
@Component
@Slf4j
public class ElasticsearchUtil {
@Resource
private RestHighLevelClient restHighLevelClient;
/**
* 生成索引对应的字段信息
*
* @param clazz
* @return
* @throws IOException
*/
private XContentBuilder generateBuilder(Class clazz) throws IOException {
// 获取索引名称及类型
Document doc = (Document) clazz.getAnnotation(Document.class);
System.out.println(doc.index());
System.out.println(doc.type());
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
builder.startObject("properties");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
if (f.isAnnotationPresent(com.example.springbootelasticsearch4.annotation.Field.class)) {
// 获取注解
com.example.springbootelasticsearch4.annotation.Field declaredAnnotation = f.getDeclaredAnnotation(com.example.springbootelasticsearch4.annotation.Field.class);
// 如果嵌套对象
if (declaredAnnotation.type() == FieldType.OBJECT) {
// 获取当前类的对象-- Action
Class<?> type = f.getType();
Field[] df2 = type.getDeclaredFields();
builder.startObject(f.getName());
builder.startObject("properties");
// 遍历该对象中的所有属性
for (Field f2 : df2) {
if (f2.isAnnotationPresent(com.example.springbootelasticsearch4.annotation.Field.class)) {
// 获取注解
com.example.springbootelasticsearch4.annotation.Field declaredAnnotation2 = f2.getDeclaredAnnotation(com.example.springbootelasticsearch4.annotation.Field.class);
builder.startObject(f2.getName());
builder.field("type", declaredAnnotation2.type().getType());
// keyword不需要分词
if (declaredAnnotation2.type() == FieldType.TEXT) {
builder.field("analyzer", declaredAnnotation2.analyzer().getType());
}
if (declaredAnnotation2.type() == FieldType.DATE) {
builder.field("format", "yyyy-MM-dd HH:mm:ss");
}
builder.endObject();
}
}
builder.endObject();
builder.endObject();
} else {
builder.startObject(f.getName());
builder.field("type", declaredAnnotation.type().getType());
// keyword不需要分词
if (declaredAnnotation.type() == FieldType.TEXT) {
builder.field("analyzer", declaredAnnotation.analyzer().getType());
}
if (declaredAnnotation.type() == FieldType.DATE) {
builder.field("format", "yyyy-MM-dd HH:mm:ss");
}
builder.endObject();
}
}
}
// 对应property
builder.endObject();
builder.endObject();
return builder;
}
/**
* 1、创建索引
* 默认分片数为5和副本数为1
*
* @param clazz 根据实体自动映射es索引
* @throws IOException
*/
public boolean createIndex(Class clazz) throws Exception {
Document declaredAnnotation = (Document) clazz.getDeclaredAnnotation(Document.class);
if (declaredAnnotation == null) {
throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", clazz.getName()));
}
String indexName = declaredAnnotation.index();
boolean indexExists = isIndexExists(indexName);
if (!indexExists) {
CreateIndexRequest request = new CreateIndexRequest(indexName);
request.settings(Settings.builder()
// 设置分片数为3, 副本为2
.put("index.number_of_shards", 3)
.put("index.number_of_replicas", 2)
);
request.mapping(generateBuilder(clazz));
CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
// 指示是否所有节点都已确认请求
boolean acknowledged = response.isAcknowledged();
// 指示是否在超时之前为索引中的每个分片启动了必需的分片副本数
boolean shardsAcknowledged = response.isShardsAcknowledged();
if (acknowledged || shardsAcknowledged) {
log.info("创建索引成功!索引名称为{}", indexName);
return true;
}
} else {
log.info("索引已经存在!索引名称为{}", indexName);
return false;
}
return false;
}
/**
* 2、判断索引是否存在
*
* @param indexName
* @return
*/
public boolean isIndexExists(String indexName) {
boolean exists = false;
try {
GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
getIndexRequest.humanReadable(true);
exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
return exists;
}
/**
* 3、更新索引
* 默认分片数为5和副本数为1
* 只能给索引上添加一些不存在的字段
* 已经存在的映射不能改
*
* @param clazz 根据实体自动映射es索引
* @throws IOException
*/
public boolean updateIndex(Class clazz) throws Exception {
Document declaredAnnotation = (Document) clazz.getDeclaredAnnotation(Document.class);
if (declaredAnnotation == null) {
throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", clazz.getName()));
}
String indexName = declaredAnnotation.index();
PutMappingRequest request = new PutMappingRequest(indexName);
request.source(generateBuilder(clazz));
AcknowledgedResponse response = restHighLevelClient.indices().putMapping(request, RequestOptions.DEFAULT);
// 指示是否所有节点都已确认请求
boolean acknowledged = response.isAcknowledged();
if (acknowledged) {
log.info("更新索引索引成功!索引名称为{}", indexName);
return true;
} else {
log.info("更新索引索引失败!索引名称为{}", indexName);
return false;
}
}
/**
* 4、删除索引
*
* @param indexName
* @return
*/
public boolean delIndex(String indexName) {
boolean acknowledged = false;
try {
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
deleteIndexRequest.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
acknowledged = delete.isAcknowledged();
} catch (IOException e) {
e.printStackTrace();
}
return acknowledged;
}
/**
* 获取字段信息
*
* @param o
* @param annotationClass
* @return
*/
private static Field getFieldByAnnotation(Object o, Class annotationClass) {
Field[] declaredFields = o.getClass().getDeclaredFields();
if (declaredFields != null && declaredFields.length > 0) {
for (Field f : declaredFields) {
if (f.isAnnotationPresent(annotationClass)) {
return f;
}
}
}
return null;
}
/**
* 5、添加单条数据
* 提供多种方式:
* 1. json
* 2. map
* Map<String, Object> jsonMap = new HashMap<>();
* jsonMap.put("user", "kimchy");
* jsonMap.put("postDate", new Date());
* jsonMap.put("message", "trying out Elasticsearch");
* IndexRequest indexRequest = new IndexRequest("posts").id("1").source(jsonMap);
* 3. builder
* XContentBuilder builder = XContentFactory.jsonBuilder();
* builder.startObject();
* {
* builder.field("user", "kimchy");
* builder.timeField("postDate", new Date());
* builder.field("message", "trying out Elasticsearch");
* }
* builder.endObject();
* IndexRequest indexRequest = new IndexRequest("posts").id("1").source(builder);
* 4. source:
* IndexRequest indexRequest = new IndexRequest("posts").id("1").source("user", "kimchy","postDate", new Date(),"message", "trying out Elasticsearch");
*
* @return
*/
public IndexResponse addDocument(Object o) throws Exception {
Document declaredAnnotation = (Document) o.getClass().getDeclaredAnnotation(Document.class);
if (declaredAnnotation == null) {
throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", o.getClass().getName()));
}
String indexName = declaredAnnotation.index();
IndexRequest request = new IndexRequest(indexName);
Field fieldByAnnotation = getFieldByAnnotation(o, EsId.class);
if (fieldByAnnotation != null) {
fieldByAnnotation.setAccessible(true);
try {
Object id = fieldByAnnotation.get(o);
request = request.id(id.toString());
} catch (IllegalAccessException e) {
log.error("获取id字段出错:{}", e);
}
}
String userJson = JSON.toJSONString(o);
request.source(userJson, XContentType.JSON);
IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
return indexResponse;
}
/**
* 6、根据id查询
*
* @return
*/
public String queryDocumentById(String indexName, String id) throws IOException {
GetRequest getRequest = new GetRequest(indexName, id);
// getRequest.fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE);
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
String jsonStr = getResponse.getSourceAsString();
return jsonStr;
}
/**
* 7、查询封装,返回json字符串
*
* @param indexName
* @param searchSourceBuilder
* @return
* @throws IOException
*/
public String search(String indexName, SearchSourceBuilder searchSourceBuilder) throws IOException {
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.source(searchSourceBuilder);
searchRequest.scroll(TimeValue.timeValueMinutes(1L));
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
String scrollId = searchResponse.getScrollId();
SearchHits hits = searchResponse.getHits();
JSONArray jsonArray = new JSONArray();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
JSONObject jsonObject = JSON.parseObject(sourceAsString);
jsonArray.add(jsonObject);
}
log.info("返回总数为:" + hits.getTotalHits());
return jsonArray.toJSONString();
}
/**
* 8、查询封装,返回带分页
*
* @param searchSourceBuilder
* @param pageNum
* @param pageSize
* @param s
* @param <T>
* @return
* @throws IOException
*/
public <T> PageInfo<T> search(SearchSourceBuilder searchSourceBuilder, int pageNum, int pageSize, Class<T> s) throws Exception {
Document declaredAnnotation = (Document) s.getDeclaredAnnotation(Document.class);
if (declaredAnnotation == null) {
throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", s.getName()));
}
String indexName = declaredAnnotation.index();
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
JSONArray jsonArray = new JSONArray();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
JSONObject jsonObject = JSON.parseObject(sourceAsString);
jsonArray.add(jsonObject);
}
log.info("返回总数为:" + hits.getTotalHits());
int total = (int) hits.getTotalHits().value;
// 封装分页
List<T> list = jsonArray.toJavaList(s);
PageInfo<T> page = new PageInfo<>();
page.setList(list);
page.setPageNum(pageNum);
page.setPageSize(pageSize);
page.setTotal(total);
page.setPages(total == 0 ? 0 : (total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1));
page.setHasNextPage(page.getPageNum() < page.getPages());
return page;
}
/**
* 9、查询封装,返回集合
*
* @param searchSourceBuilder
* @param s
* @param <T>
* @return
* @throws IOException
*/
public <T> List<T> search(SearchSourceBuilder searchSourceBuilder, Class<T> s) throws Exception {
Document declaredAnnotation = (Document) s.getDeclaredAnnotation(Document.class);
if (declaredAnnotation == null) {
throw new Exception(String.format("class name: %s can not find Annotation [Document], please check", s.getName()));
}
String indexName = declaredAnnotation.index();
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.source(searchSourceBuilder);
searchRequest.scroll(TimeValue.timeValueMinutes(1L));
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
String scrollId = searchResponse.getScrollId();
SearchHits hits = searchResponse.getHits();
JSONArray jsonArray = new JSONArray();
for (SearchHit hit : hits) {
String sourceAsString = hit.getSourceAsString();
JSONObject jsonObject = JSON.parseObject(sourceAsString);
jsonArray.add(jsonObject);
}
// 封装分页
List<T> list = jsonArray.toJavaList(s);
return list;
}
/**
* 10、批量插入文档
* 文档存在则更新
* 文档不存在则插入
*
* @param list
* @return
*/
public <T> boolean batchSaveOrUpdate(List<T> list) throws Exception {
Object o1 = list.get(0);
Document declaredAnnotation = (Document) o1.getClass().getDeclaredAnnotation(Document.class);
if (declaredAnnotation == null) {
throw new Exception(String.format("class name: %s can not find Annotation [@Document], please check", o1.getClass().getName()));
}
String indexName = declaredAnnotation.index();
BulkRequest request = new BulkRequest(indexName);
for (Object o : list) {
String jsonStr = JSON.toJSONString(o);
IndexRequest indexReq = new IndexRequest().source(jsonStr, XContentType.JSON);
Field fieldByAnnotation = getFieldByAnnotation(o, EsId.class);
if (fieldByAnnotation != null) {
fieldByAnnotation.setAccessible(true);
try {
Object id = fieldByAnnotation.get(o);
indexReq = indexReq.id(id.toString());
} catch (IllegalAccessException e) {
log.error("获取id字段出错:{}", e);
}
}
request.add(indexReq);
}
BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
for (BulkItemResponse bulkItemResponse : bulkResponse) {
DocWriteResponse itemResponse = bulkItemResponse.getResponse();
IndexResponse indexResponse = (IndexResponse) itemResponse;
log.info("单条返回结果:{}", indexResponse);
if (bulkItemResponse.isFailed()) {
log.error("es 返回错误{}", bulkItemResponse.getFailureMessage());
return false;
}
}
return true;
}
/**
* 11、删除文档
*
* @param indexName: 索引名称
* @param docId: 文档id
*/
public boolean deleteDoc(String indexName, String docId) throws IOException {
DeleteRequest request = new DeleteRequest(indexName, docId);
DeleteResponse deleteResponse = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
// 解析response
String index = deleteResponse.getIndex();
String id = deleteResponse.getId();
long version = deleteResponse.getVersion();
ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
if (shardInfo.getFailed() > 0) {
for (ReplicationResponse.ShardInfo.Failure failure :
shardInfo.getFailures()) {
String reason = failure.reason();
log.info("删除失败,原因为 {}", reason);
}
}
return true;
}
/**
* 12、根据json类型更新文档
*
* @param indexName
* @param docId
* @param o
* @return
* @throws IOException
*/
public boolean updateDoc(String indexName, String docId, Object o) throws IOException {
UpdateRequest request = new UpdateRequest(indexName, docId);
request.doc(JSON.toJSONString(o), XContentType.JSON);
UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);
String index = updateResponse.getIndex();
String id = updateResponse.getId();
long version = updateResponse.getVersion();
if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
return true;
} else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
return true;
} else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) {
return false;
} else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) {
return false;
}
return false;
}
/**
* 13、根据Map类型更新文档
*
* @param indexName
* @param docId
* @param map
* @return
* @throws IOException
*/
public boolean updateDoc(String indexName, String docId, Map<String, Object> map) throws IOException {
UpdateRequest request = new UpdateRequest(indexName, docId);
request.doc(map);
UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);
String index = updateResponse.getIndex();
String id = updateResponse.getId();
long version = updateResponse.getVersion();
if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
return true;
} else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
return true;
} else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) {
return false;
} else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) {
return false;
}
return false;
}
}
6、实体
package com.example.springbootelasticsearch4.pojo;
import com.example.springbootelasticsearch4.annotation.*;
import lombok.Data;
/**
* @author zhangshixing
* @date 2022年01月16日 9:53
* @Description ElasticSearch索引实体类
*/
@Data
@Document(index = "search_index", type = "")
public class Search {
/**
* 商品id
*/
@EsId
@Field(type = FieldType.INTEGER)
private Integer itemId;
/**
* 商品名字
*/
@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_MAX_WORD)
private String itemName;
/**
* 价格
* 要进行范围筛选
*/
@Field(type = FieldType.DOUBLE)
private Double price;
/**
* 规格名称
* 多个规格组合
*/
@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_MAX_WORD)
private String itemSpecName;
/**
* 产地
*/
@Field(type = FieldType.KEYWORD)
private String productPlace;
/**
* 保质期
* 200天
* 要进行范围筛选
*/
@Field(type = FieldType.INTEGER)
private Integer footPeriod;
/**
* 品牌
*/
@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_MAX_WORD)
private String brand;
/**
* 包装方式
*/
@Field(type = FieldType.KEYWORD)
private String packagingMethod;
/**
* 商品重量
* 25g
* 要进行范围筛选
*/
@Field(type = FieldType.DOUBLE)
private Integer weight;
/**
* 食用方式
*/
@Field(type = FieldType.KEYWORD)
private String eatMethod;
/**
* 销量
* 要进行范围筛选
*/
@Field(type = FieldType.INTEGER)
private Integer sellCounts;
/**
* 口味
* 多种口味组合
*/
@Field(type = FieldType.TEXT, analyzer = AnalyzerType.IK_MAX_WORD)
private String flavor;
/**
* 图片的展示图片
*/
@Field(type = FieldType.KEYWORD)
private String showImg;
}
这里会使用反射通过上面标注的注解来使用:比如我有一个实体,根据这个实体自动创建索引的方式如下; 首先
在实体上加入我们自定义的注解,来设置索引名称,字段的类型,分词器是什么。这里字符串类型在es中有两
种,一是KEY_WORD,不分词, 二是TEXT,会分词。
package com.example.springbootelasticsearch4.pojo;
import lombok.*;
/**
* 将查询条件和分页条件都封装到一个VO中。
*/
@Data
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class SearchVO {
/**
* 商品id
*/
private Integer itemId;
/**
* 商品名字
*/
private String itemName;
/**
* 价格
* 要进行范围筛选
*/
private Double price;
/**
* 规格名称
* 多个规格组合
*/
private String itemSpecName;
/**
* 产地
*/
private String productPlace;
/**
* 保质期
* 200天
* 要进行范围筛选
*/
private Integer footPeriod;
/**
* 品牌
*/
private String brand;
/**
* 包装方式
*/
private String packagingMethod;
/**
* 商品重量
* 25g
* 要进行范围筛选
*/
private Integer weight;
/**
* 食用方式
*/
private String eatMethod;
/**
* 销量
* 要进行范围筛选
*/
private Integer sellCounts;
/**
* 口味
* 多种口味组合
*/
private String flavor;
/**
* 图片的展示图片
*/
private String showImg;
/**
* 当前第几页
*/
private Integer pageNum;
/**
* 每页显示多少条数据
*/
private Integer pageSize;
/**
* 排序的字段名
*/
private String sortName;
/**
* 排的方式
* 降序或者是升序
*/
private String orderSort;
}
7、Service
package com.example.springbootelasticsearch4.service;
import com.example.springbootelasticsearch4.pojo.SearchVO;
import com.github.pagehelper.PageInfo;
public interface QueryService {
public PageInfo<SearchVO> pageSearchQuery(SearchVO searchVO) throws Exception;
}
package com.example.springbootelasticsearch4.service.impl;
import com.example.springbootelasticsearch4.pojo.Search;
import com.example.springbootelasticsearch4.pojo.SearchVO;
import com.example.springbootelasticsearch4.service.QueryService;
import com.example.springbootelasticsearch4.utils.ElasticsearchUtil;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
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;
/**
* @author zhangshixing
* @date 2021年11月11日 20:45
*/
@Service
public class QueryServiceImpl implements QueryService {
@Autowired
private ElasticsearchUtil elasticsearchUtil;
@Override
public PageInfo<SearchVO> pageSearchQuery(SearchVO searchVO) throws Exception {
SearchSourceBuilder searchSourceBuilder = getSearchSourceBuilder(searchVO);
PageInfo page = elasticsearchUtil.search(searchSourceBuilder, searchVO.getPageNum(), searchVO.getPageSize(), Search.class);
return page;
}
/**
* 拼接综合查询 查询条件
*
* @param searchVO
* @return
*/
private SearchSourceBuilder getSearchSourceBuilder(SearchVO searchVO) {
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 设置第几页
if (searchVO.getPageNum() == null) {
searchVO.setPageNum(1);
}
// 设置每页显示的数量
if (searchVO.getPageSize() == null) {
searchVO.setPageSize(10000);
}
// 设置页数
sourceBuilder.from((searchVO.getPageNum() - 1) * searchVO.getPageSize());
sourceBuilder.size(searchVO.getPageSize());
// 符合条件查询
BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
if (StringUtils.isNotEmpty(searchVO.getItemName())) {
boolBuilder.must(QueryBuilders.matchQuery("itemName", searchVO.getItemName()));
}
if (StringUtils.isNotEmpty(searchVO.getItemSpecName())) {
boolBuilder.must(QueryBuilders.termQuery("itemSpecName", searchVO.getItemSpecName()));
}
if (searchVO.getPrice() != null) {
boolBuilder.must(QueryBuilders.rangeQuery("price").gte(searchVO.getPrice()));
}
sourceBuilder.query(boolBuilder);
// 排序
if (StringUtils.isNotEmpty(searchVO.getSortName())) {
FieldSortBuilder fieldSortBuilder = new FieldSortBuilder(searchVO.getSortName());
// 按照升序还是降序排序
fieldSortBuilder = fieldSortBuilder.order("orderDesc".equals(searchVO.getOrderSort()) ? SortOrder.DESC : SortOrder.ASC);
sourceBuilder.sort(fieldSortBuilder);
}
return sourceBuilder;
}
}
8、启动类
package com.example.springbootelasticsearch4;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootElasticsearch4Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootElasticsearch4Application.class, args);
}
}
9、测试
package com.example.springbootelasticsearch4;
import com.example.springbootelasticsearch4.pojo.Search;
import com.example.springbootelasticsearch4.pojo.SearchVO;
import com.example.springbootelasticsearch4.service.QueryService;
import com.example.springbootelasticsearch4.utils.ElasticsearchUtil;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@Slf4j
@SpringBootTest
class SpringBootElasticsearch4ApplicationTests {
@Autowired
private ElasticsearchUtil elasticsearchUtil;
@Autowired
private QueryService queryService;
@Test
void contextLoads() {
try {
elasticsearchUtil.createIndex(Search.class);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
void contextLoads1() {
// 这里可以设置一些属性
SearchVO searchVO = new SearchVO();
PageInfo<SearchVO> returnPageInfo = null;
try {
returnPageInfo = queryService.pageSearchQuery(searchVO);
} catch (Exception e) {
log.error("es 综合查询异常,开始使用数据库做综合查询,错误为 :{}", e);
// es异常,使用数据库查询
// 进行数据库的一些查询操作
// returnPageInfo = 数据库操作
}
System.out.println(returnPageInfo);
}
}
这样就会根据创建一个叫做search_index
的索引(实体注解上配置),并且可以设置一些查询条件进行查询。