ElasticSearch和SpringBoot
ElasticSearch和SpringBoot联合使用,可以使用Java原生提供的RestHighLevelClient进行ElasticSearch的所有操作。
也可以使用CrudRepository对ElasticSearch执行简单的增删改查操作。
项目项目
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <elasticsearch.version>7.15.2</elasticsearch.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-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>compile</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.5.8</version> </dependency> </dependencies>
|
application.yml
1 2 3 4 5 6 7 8
| server: port: 12051 spring: application: name: springboot-elasticsearch elasticsearch: rest: uris: api.es.com:9200
|
ElasticSearchConfig.java
1 2 3 4 5 6 7 8 9
| @Configuration public class ElasticSearchConfig {
@Bean public RestHighLevelClient getRestHighLevelClient() { return new RestHighLevelClient(RestClient.builder(new HttpHost("api.es.com", 9200, "http"))); }
}
|
ElasticSearchApplication.java
1 2 3 4 5 6 7 8 9 10 11
|
@SpringBootApplication public class ElasticSearchApplication {
public static void main(String[] args) { SpringApplication.run(ElasticSearchApplication.class); }
}
|
UserSalary.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Data @Document(indexName = "user_salary") public class UserSalary {
@Id private String id;
private String name;
private String deployment;
private Integer salary;
private Long createTime;
private Long updateTime;
}
|
SnowflakesUtil.java
1 2 3 4 5 6 7 8 9 10
| public class SnowflakesUtil {
private static final Long WORKER_ID = 1L; private static final Long DATACENTER_ID = 1L;
public static Long getNextId() { return IdUtil.getSnowflake(WORKER_ID, DATACENTER_ID).nextId(); }
}
|
操作索引
IndexServiceImpl.java
1 2 3 4 5 6 7 8
| @Slf4j @Service public class IndexServiceImpl implements IndexService {
@Resource private RestHighLevelClient client;
}
|
创建索引
1 2 3 4 5 6
| public void createIndex(String index) throws IOException { log.info("index = {}", index); CreateIndexRequest request = new CreateIndexRequest(index); CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT); log.info("response.isAcknowledged() = {}", response.isAcknowledged()); }
|
删除索引
1 2 3 4 5 6
| public void deleteIndex(String index) throws IOException { log.info("index = {}", index); DeleteIndexRequest request = new DeleteIndexRequest(index); AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT); log.info("response.isAcknowledged() = {}", response.isAcknowledged()); }
|
查询索引
1 2 3 4 5
| public void searchIndex(String index) throws IOException { GetIndexRequest request = new GetIndexRequest(index); GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT); log.info("response.getAliases() = {}", response.getAliases()); }
|
操作文档
DocumentServiceImpl.java
1 2 3 4 5 6 7 8
| @Slf4j @Service public class DocumentServiceImpl implements DocumentService {
@Resource private RestHighLevelClient client;
}
|
创建文档
1 2 3 4 5 6 7 8 9 10 11
| public void create(String index, UserSalary userSalary) throws IOException { String id = String.valueOf(SnowflakesUtil.getNextId()); userSalary.setCreateTime(System.currentTimeMillis()); userSalary.setUpdateTime(System.currentTimeMillis()); userSalary.setId(id); IndexRequest request = new IndexRequest(); request.index(index).id(id); request.source(BeanUtil.beanToMap(userSalary)); IndexResponse response = client.index(request, RequestOptions.DEFAULT); System.out.println("_result:" + response.getResult()); }
|
批量创建文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| public void createBatch(String index) throws IOException { BulkRequest request = new BulkRequest(); List<UserSalary> list = getUserSalaryList();
for (UserSalary userSalary : list) { String str = JSONUtil.toJsonStr(userSalary); request.add(new IndexRequest().index(index).id(userSalary.getId()).source(str, XContentType.JSON)); } BulkResponse response = client.bulk(request, RequestOptions.DEFAULT); System.out.println("took = " + response.getTook()); System.out.println("items = " + ArrayUtil.join(response.getItems(), ", ")); }
private List<UserSalary> getUserSalaryList() { List<UserSalary> list = CollUtil.newArrayList(); long now = System.currentTimeMillis(); UserSalary userSalary1 = new UserSalary(); userSalary1.setId(String.valueOf(SnowflakesUtil.getNextId())); userSalary1.setName("蒋一一"); userSalary1.setDeployment("客服"); userSalary1.setSalary(800000); userSalary1.setCreateTime(now); userSalary1.setUpdateTime(now);
UserSalary userSalary2 = new UserSalary(); userSalary2.setId(String.valueOf(SnowflakesUtil.getNextId())); userSalary2.setName("沈阳"); userSalary2.setDeployment("客服"); userSalary2.setSalary(900000); userSalary2.setCreateTime(now); userSalary2.setUpdateTime(now);
UserSalary userSalary3 = new UserSalary(); userSalary3.setId(String.valueOf(SnowflakesUtil.getNextId())); userSalary3.setName("韩金龙"); userSalary3.setDeployment("老板"); userSalary3.setSalary(100000000); userSalary3.setCreateTime(now); userSalary3.setUpdateTime(now);
list.add(userSalary1); list.add(userSalary2); list.add(userSalary3); return list; }
|
修改文档
1 2 3 4 5 6 7 8 9 10 11 12 13
| public void update(String index, UserSalary userSalary) throws IOException { Long now = System.currentTimeMillis(); userSalary.setUpdateTime(now); UpdateRequest request = new UpdateRequest(); request.id(userSalary.getId()); request.index(index); Map<String, Object> map = BeanUtil.beanToMap(userSalary); request.doc(map); request.docAsUpsert(true); UpdateResponse response = client.update(request, RequestOptions.DEFAULT); System.out.println("_result:" + response.getResult()); }
|
删除文档
1 2 3 4 5
| public void delete(String index, String id) throws IOException { DeleteRequest request = new DeleteRequest().index(index).id(id); DeleteResponse response = client.delete(request, RequestOptions.DEFAULT); System.out.println("response = " + response.toString()); }
|
查询文档
查询文档的操作有很多:条件、分页、排序、组合条件(and, or)、通配符、范围、正则表达式、高亮、最大最小平均值、分组查询等
查询所有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public List<UserSalary> query() throws IOException { String index = "user_salary"; List<UserSalary> list = CollUtil.newArrayList(); SearchRequest request = new SearchRequest(index); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.query(QueryBuilders.matchAllQuery()); System.out.println("【查询操作】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询结果】 - hit = " + map.toString()); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); list.add(userSalary); }); return list; }
|
组合查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
public List<UserSalary> query2() throws IOException { List<UserSalary> list = CollUtil.newArrayList(); SearchRequest request = new SearchRequest(); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); BoolQueryBuilder java = new BoolQueryBuilder(); BoolQueryBuilder test = new BoolQueryBuilder(); BoolQueryBuilder should = new BoolQueryBuilder(); MatchQueryBuilder matchJava = QueryBuilders.matchQuery("deployment", "Java"); RangeQueryBuilder rangeJava = QueryBuilders.rangeQuery("salary").gte(1300000); java.must(matchJava).must(rangeJava); MatchQueryBuilder matchTest = QueryBuilders.matchQuery("deployment", "测试"); RangeQueryBuilder rangeTest = QueryBuilders.rangeQuery("salary").gte(1000000); test.must(matchTest).must(rangeTest); should.should(java).should(test); sourceBuilder.query(should); System.out.println("【查询操作(2)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询操作(2)】 - 【结果】 - hit = " + map.toString()); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); list.add(userSalary); }); return list; }
|
通配符查询
* : 相当于0个或多个字符
? : 相当于1个字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public List<UserSalary> query3() throws IOException { List<UserSalary> list = CollUtil.newArrayList(); SearchRequest request = new SearchRequest(); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.query(QueryBuilders.wildcardQuery("deployment", "*程*")); System.out.println("【查询操作(3)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询操作(3)】 - 【结果】 - hit = " + map.toString()); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); list.add(userSalary); }); return list; }
|
多值查询
相当于SQL中的in操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public List<UserSalary> query4() throws IOException { List<UserSalary> list = CollUtil.newArrayList(); SearchRequest request = new SearchRequest(); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.query(QueryBuilders.termsQuery("id", "1470290808988110848", "1470282535723470848")); System.out.println("【查询操作(4)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询操作(4)】 - 【结果】 - hit = " + map.toString()); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); list.add(userSalary); }); return list; }
|
存在查询
相当于SQL中的exist查询操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public List<UserSalary> query5() throws IOException { List<UserSalary> list = CollUtil.newArrayList(); String index = "user_salary"; SearchRequest request = new SearchRequest(index); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.query(QueryBuilders.existsQuery("salary")); System.out.println("【查询操作(5)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询操作(5)】 - 【结果】 - hit = " + map.toString()); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); list.add(userSalary); }); return list; }
|
范围查询
相当于SQL中的between
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public List<UserSalary> query6() throws IOException { List<UserSalary> list = CollUtil.newArrayList(); SearchRequest request = new SearchRequest(); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.query(QueryBuilders.rangeQuery("createTime") .gte(1639381811162L).lte(1639448670000L)); sourceBuilder.sort("createTime", SortOrder.DESC); System.out.println("【查询操作(6)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询操作(6)】 - 【结果】 - hit = " + map.toString()); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); list.add(userSalary); }); return list; }
|
正则查询
(有问题,但是直接在kibana中查询没问题)
更复杂的通配符查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public List<UserSalary> query7() throws IOException { List<UserSalary> list = CollUtil.newArrayList(); String index = "user_salary"; SearchRequest request = new SearchRequest(index); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.query(QueryBuilders.regexpQuery("deployment", "[a-zA-Z]{4}开发工程师")); System.out.println("【查询操作(7)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询操作(7)】 - 【结果】 - hit = " + map.toString()); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); list.add(userSalary); }); return list; }
|
模糊查询
fuzziness模糊查询操作,我也没看这是干啥的,用一下这个也没看懂这里面的一个参数是干啥的Fuzziness.ZERO, Fuzziness.ONE, Fuzziness.TWO, Fuzziness.AUTO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public List<UserSalary> query8() throws IOException { List<UserSalary> list = CollUtil.newArrayList(); SearchRequest request = new SearchRequest(); request.indices("user_salary");
SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.query(QueryBuilders.fuzzyQuery("deployment", "开发工程师"). fuzziness(Fuzziness.ONE)); System.out.println("【查询操作(8)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询操作(8)】 - 【结果】 - hit = " + map.toString()); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); list.add(userSalary); }); return list; }
|
高亮查询
在查询结果的字段中加入高亮的html标签,该标签可自定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public List<UserSalary> query9() throws IOException { List<UserSalary> list = CollUtil.newArrayList(); String index = "user_salary"; SearchRequest request = new SearchRequest().indices(index); HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.preTags("<font color='red'>"); highlightBuilder.postTags("</font>"); highlightBuilder.field("deployment"); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.highlighter(highlightBuilder); sourceBuilder.query(QueryBuilders.matchQuery("deployment", "Java和前端")); System.out.println("【查询操作(9)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); response.getHits().forEach(hit -> { Map<String, Object> map = hit.getSourceAsMap(); System.out.println("【查询操作(9)】 - 【结果】 - hit = " + map.toString()); Map<String, HighlightField> highlightFields = hit.getHighlightFields(); HighlightField deployment = highlightFields.get("deployment"); UserSalary userSalary = BeanUtil.toBeanIgnoreError(map, UserSalary.class); System.out.println(deployment.fragments()[0]); System.out.println("StrUtil.toString(deployment.fragments()) = " + StrUtil.toString(deployment.fragments())); userSalary.setDeployment(deployment.getFragments()[0].toString()); list.add(userSalary); }); return list; }
|
最高值查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
public JSONObject query10() throws IOException { String index = "user_salary"; SearchRequest request = new SearchRequest(index); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.aggregation(AggregationBuilders.max("maxSalary").field("salary")); sourceBuilder.aggregation(AggregationBuilders.min("minSalary").field("salary")); sourceBuilder.aggregation(AggregationBuilders.avg("avgSalary").field("salary")); sourceBuilder.size(0); System.out.println("【查询操作(10)】 - 【查询语句】:" + sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); System.out.println("【查询操作(10)】 - 【查询结果】:" + response.toString());
Map<String, Aggregation> map = response.getAggregations().getAsMap();
Aggregation maxSalary = map.get("maxSalary"); JSONObject maxSalaryJson = JSONUtil.parseObj(maxSalary); String maxSalaryName = maxSalaryJson.getStr("name"); Long maxSalaryValue = maxSalaryJson.getLong("value"); System.out.println("maxSalary : name = " + maxSalaryName + ", value = " + maxSalaryValue);
Aggregation minSalary = map.get("minSalary"); JSONObject minSalaryJson = JSONUtil.parseObj(minSalary); String minSalaryName = minSalaryJson.getStr("name"); Long minSalaryValue = minSalaryJson.getLong("value"); System.out.println("minSalary : name = " + minSalaryName + ", value = " + minSalaryValue);
Aggregation avgSalary = map.get("avgSalary"); JSONObject avgSalaryJson = JSONUtil.parseObj(avgSalary); String avgSalaryName = avgSalaryJson.getStr("name"); Long avgSalaryValue = avgSalaryJson.getLong("value"); System.out.println("avgSalary : name = " + avgSalaryName + ", value = " + avgSalaryValue);
JSONObject result = new JSONObject(); result.set(maxSalaryName, maxSalaryValue); result.set(minSalaryName, minSalaryValue); result.set(avgSalaryName, avgSalaryValue);
return result; }
|
分组查询
分组查询,获取每一组的文档数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
public JSONObject query11() throws IOException { String index = "user_salary"; SearchRequest request = new SearchRequest(index); SearchSourceBuilder sourceBuilder = SearchSourceBuilder.searchSource(); sourceBuilder.aggregation(AggregationBuilders.terms("salary_group").field("salary")); sourceBuilder.size(0); log.info("【查询操作(11)】 - 【查询语句】:{}", sourceBuilder.toString()); request.source(sourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); System.out.println("【查询操作(11)】 - 【查询结果】:" + response.toString()); System.out.println(response.toString()); Map<String, Aggregation> map = response.getAggregations().getAsMap(); System.out.println(JSONUtil.toJsonStr(map)); Aggregation salaryGroup = map.get("salary_group"); JSONObject salaryGroupJson = JSONUtil.parseObj(salaryGroup); JSONArray buckets = salaryGroupJson.getJSONArray("buckets"); System.out.println(buckets.toString());
JSONObject result = new JSONObject(); for (int i = 0; i < buckets.size(); i++) { JSONObject bucket = buckets.getJSONObject(i); Long key = bucket.getLong("key"); Integer count = bucket.getInt("docCount"); result.set(String.valueOf(key), count); } return result; }
|
Repository操作文档
Repository进行增删改查应该都很熟悉了:save, delete, findById……等方法比较常用。其中仅提供了比较基础的增删改查方法,更复杂的查询方法,还是需要用RestHighLevelClient去完成。