1. Low Level & High Level

https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html


SpringBoot集成es,可以选择

1. Java Low Level REST Client 特点是灵活,缺点暴露的api太多,太木乱!

2. Java High Level REST Client 特点是相对简单,缺点是相对不灵活!但是绝对够用!


我们选择 High Level 的这一个,因为它封装地更好,屏蔽了更多的底层逻辑。



2. SpringBoot集成ElasticSearch


2.1 pom.xml

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.4.2</version>

</parent>


<properties>

<java.version>1.8</java.version>

<elasticsearch.version>7.11.2</elasticsearch.version>

</properties>


<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.elasticsearch.client</groupId>

<artifactId>elasticsearch-rest-high-level-client</artifactId>

<version>7.10.2</version>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

</dependency>

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

</dependency>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

<version>1.2.79</version></dependency>

</dependencies>


2.2 配置类

@Configuration

public class ElasticSearchConfiguration {

@Bean

public RestHighLevelClient restHighLevelClient() {

RestHighLevelClient client = new RestHighLevelClient(

RestClient.builder(

new HttpHost("192.168.163.129", 9200, "http")

)

);

return client;

}

}


2.3 启动类

@SpringBootApplication

public class App {

public static void main(String[] args) {

SpringApplication.run(App.class, args);

}

}


2.4 测试环境

@SpringBootTest

public class AppTest {


@Autowired

private RestHighLevelClient client;


@Test

public void testEnv() {

System.out.println(client);

}


}


6. SpringBoot集成ElasticSearch_System



2.5 实体类

@Data

@NoArgsConstructor

@AllArgsConstructor

public class User {

private Integer id;

private String name;

private Date birthday;

private Double balance;

}



3. 各种测试


3.1 索引基本操作

/**

创建索引

*/

@Test

public void createIndexTest() throws IOException {

准备好“创建索引”的请求

CreateIndexRequest request = new CreateIndexRequest("foo");

向es发送请求,并接受es的响应

CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);

System.out.println("response = " + response.isAcknowledged());

}


/**

判断索引是否存在

*/

@Test

public void getIndexTest() throws IOException {

GetIndexRequest request = new GetIndexRequest("foo");

boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);

System.out.println("exists = " + exists);

}


/**

删除索引

*/

@Test

public void deleteIndexTest() throws IOException {

DeleteIndexRequest request = new DeleteIndexRequest("foo");

AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);

System.out.println("response = " + response.isAcknowledged());

}


package com.ruoyi.controller;import com.alibaba.fastjson.JSON;import com.ruoyi.entiry.User;import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;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.master.AcknowledgedResponse;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.common.unit.TimeValue;import org.elasticsearch.common.xcontent.XContentType;import org.elasticsearch.search.SearchHit;import org.elasticsearch.search.SearchHits;import org.elasticsearch.search.builder.SearchSourceBuilder;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.io.IOException;import java.util.Date;import java.util.Map;/** * @author gao * @date 2023/11/09 11:19:26 */@RestControllerpublic class EsController { @Autowired private RestHighLevelClient client; /** * 创建索引 */ @GetMapping("t1") public void createIndexTest() throws IOException { // 准备好“创建索引”的请求 CreateIndexRequest request = new CreateIndexRequest("foo"); // 向es发送请求,并接受es的响应 CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT); System.out.println("response = " + response.isAcknowledged()); } /** * 判断索引是否存在 */ @GetMapping("t2") public void getIndexTest() throws IOException { GetIndexRequest request = new GetIndexRequest("foo"); boolean exists = client.indices().exists(request, RequestOptions.DEFAULT); System.out.println("exists = " + exists); } /** * 删除索引 */ @GetMapping("t3") public void deleteIndexTest() throws IOException { DeleteIndexRequest request = new DeleteIndexRequest("foo"); AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT); System.out.println("response = " + response.isAcknowledged()); } /** * 添加文档 */ @GetMapping("t4") public void createDocTest() throws IOException { // 创建文档数据 User user = new User(1, "andy", new Date(), 5000d); // 创建请求,同时指定要向哪个索引添加文档 IndexRequest request = new IndexRequest("foo"); // 向请求中封装文档的id request.id(user.getId() + ""); // 设置超时时间 request.timeout("60s"); // 将文档数据格式化为json,再封装到请求中 request.source(JSON.toJSONString(user), XContentType.JSON); // 向es服务发送请求 IndexResponse response = client.index(request, RequestOptions.DEFAULT); System.out.println(response.toString()); System.out.println(response.status()); } /** * 判断文档是否存在 */ @GetMapping("t5") public void docExistsTest() throws IOException { GetRequest request = new GetRequest("foo", "1"); boolean exists = client.exists(request, RequestOptions.DEFAULT); System.out.println("exists = " + exists); } /** * 获得文档信息 */ @GetMapping("t6") public void getDocTest() throws IOException { GetRequest request = new GetRequest("foo", "1"); GetResponse response = client.get(request, RequestOptions.DEFAULT); // 打印文档内容 System.out.println(response.getSourceAsString()); System.out.println(response); } /** * 更新文档信息 */ @GetMapping("t7") public void updateDocTest() throws IOException { UpdateRequest request = new UpdateRequest("foo", "1"); request.timeout("60s"); User user = new User(10, "eason", new Date(), 6000d); request.doc(JSON.toJSONString(user), XContentType.JSON); UpdateResponse response = client.update(request, RequestOptions.DEFAULT); System.out.println(response.status()); } /** * 删除文档 */ @GetMapping("t8") public void deleteDocTest() throws IOException { DeleteRequest request = new DeleteRequest("foo", "1"); request.timeout("60s"); DeleteResponse response = client.delete(request, RequestOptions.DEFAULT); System.out.println(response.status()); } /** * 批量删除 */ @GetMapping("t9") void deleteBatch() throws IOException { BulkRequest request = new BulkRequest(); request.add(new DeleteRequest("foo", "1")); request.add(new DeleteRequest("foo", "2")); request.add(new DeleteRequest("foo", "3")); BulkResponse response = client.bulk(request, RequestOptions.DEFAULT); System.out.println(response); } /** * 批量索引 */ @GetMapping("t10") public void batchIndex() throws IOException { BulkRequest bulkRequest = new BulkRequest(); for (int i = 0; i < 20; i++) { User user = new User(i, "G.E.M" + i, new Date(), 6000d); bulkRequest.add(new IndexRequest("foo").id((i + 1) + "").source(JSON.toJSONString(user), XContentType.JSON)); } BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT); System.out.println(response); } /** * 查询多个文档 */ @GetMapping("t11") public void searchAll() throws IOException { SearchRequest request = new SearchRequest("foo"); // 构建查询条件 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); // 分页 searchSourceBuilder.from(1); searchSourceBuilder.size(2); // 查询条件 searchSourceBuilder.query(null); searchSourceBuilder.timeout(TimeValue.timeValueSeconds(10)); request.source(searchSourceBuilder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); // 获取结果 SearchHits hits = response.getHits(); System.out.println("总记录数:" + hits.getTotalHits()); for (SearchHit hit : hits) { Map<String, Object> map = hit.getSourceAsMap(); System.out.println(map); } }}

3.2 文档基本操作

/**

添加文档

*/

@Test

public void createDocTest() throws IOException {

创建文档数据

User user = new User(1, "andy", new Date(), 5000d);


创建请求,同时指定要向哪个索引添加文档

IndexRequest request = new IndexRequest("foo");

向请求中封装文档的id

request.id(user.getId() + "");

设置超时时间

request.timeout("60s");


将文档数据格式化为json,再封装到请求中

request.source(JSON.toJSONString(user), XContentType.JSON);


向es服务发送请求

IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);


System.out.println(response.toString());

System.out.println(response.status());

}


/**

判断文档是否存在

*/

@Test

public void docExistsTest() throws IOException {

GetRequest request = new GetRequest("foo", "1");

boolean exists = restHighLevelClient.exists(request, RequestOptions.DEFAULT);

System.out.println("exists = " + exists);

}


/**

获得文档信息

*/

@Test

public void getDocTest() throws IOException {


GetRequest request = new GetRequest("foo", "1");

GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);

打印文档内容

System.out.println(response.getSourceAsString());

System.out.println(response);

}


/**

更新文档信息

*/

@Test

public void updateDocTest() throws IOException {

UpdateRequest request = new UpdateRequest("foo", "1");

request.timeout("60s");


User user = new User(10, "eason", new Date(), 6000d);

request.doc(JSON.toJSONString(user), XContentType.JSON);


UpdateResponse response = restHighLevelClient.update(request, RequestOptions.DEFAULT);

System.out.println(response.status());

}


/**

删除文档

*/

@Test

public void deleteDocTest() throws IOException {

DeleteRequest request = new DeleteRequest("foo", "1");

request.timeout("60s");


DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);

System.out.println(response.status());

}


/**

批量删除

*/

@Test

void deleteBatch() throws IOException {

BulkRequest request = new BulkRequest();

request.add(new DeleteRequest("foo","1"));

request.add(new DeleteRequest("foo","2"));

request.add(new DeleteRequest("foo","3"));

BulkResponse response =

restHighLevelClient.bulk(request, RequestOptions.DEFAULT);

System.out.println(response);

}


/**

批量索引

*/

@Test

public void batchIndex() throws IOException {

BulkRequest bulkRequest = new BulkRequest();


for (int i = 0; i < 20; i++) {

User user = new User(i, "G.E.M" + i, new Date(), 6000d);

bulkRequest.add(new IndexRequest("foo").id((i + 1) + "").source(JSON.toJSONString(user), XContentType.JSON));

}


BulkResponse response = client.bulk(bulkRequest, RequestOptions.DEFAULT);

System.out.println(response);

}


/**

查询多个文档

*/

@Test

public void searchAll() throws IOException {

SearchRequest request = new SearchRequest("foo");


构建查询条件

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


分页

searchSourceBuilder.from(1);

searchSourceBuilder.size(2);


查询条件

searchSourceBuilder.query(null);

searchSourceBuilder.timeout(TimeValue.timeValueSeconds(10));


request.source(searchSourceBuilder);


SearchResponse response = client.search(request, RequestOptions.DEFAULT);

获取结果

SearchHits hits = response.getHits();

总记录数:" + hits.getTotalHits());

for (SearchHit hit : hits) {

Map<String, Object> map = hit.getSourceAsMap();

System.out.println(map);

}

}



3.3 matchQuery和termQuery

matchQuery:会对“搜索词”进行分词,再与目标字段对应的索引进行匹配

termQuery:不会对“搜索词”进行分词,直接与目标字段对应的索引进行匹配


matchQuery

@Test

public void test() throws IOException {

SearchRequest request = new SearchRequest("bank");


SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

searchSourceBuilder.query(QueryBuilders.matchQuery("address", "mill"));


request.source(searchSourceBuilder);


SearchResponse response = client.search(request, RequestOptions.DEFAULT);


SearchHits hits = response.getHits();

for (SearchHit hit : hits) {

Map<String, Object> map = hit.getSourceAsMap();

System.out.println(map);

}

}


termQuery

@Test

public void test() throws IOException {

SearchRequest request = new SearchRequest("bank");


SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

无结果

// searchSourceBuilder.query(QueryBuilders.termQuery("address", "990 Mill Road"));

有结果

searchSourceBuilder.query(QueryBuilders.termQuery("address.keyword", "990 Mill Road"));


request.source(searchSourceBuilder);


SearchResponse response = client.search(request, RequestOptions.DEFAULT);


SearchHits hits = response.getHits();

for (SearchHit hit : hits) {

Map<String, Object> map = hit.getSourceAsMap();

System.out.println(map);

}

}


3.4 单条件term,匹配多值

@Test

public void test() throws IOException {

SearchRequest request = new SearchRequest("bank");


SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

查询gender为F或M的账户

searchSourceBuilder.query(QueryBuilders.termsQuery("gender.keyword", "F", "M"));


request.source(searchSourceBuilder);


SearchResponse response = client.search(request, RequestOptions.DEFAULT);


SearchHits hits = response.getHits();

System.out.println("total: " + hits.getTotalHits());

for (SearchHit hit : hits) {

Map<String, Object> map = hit.getSourceAsMap();

System.out.println(map);

}

}

等价的命令

GET bank/_search

{

"query": {

"terms": {

"gender.keyword": ["F", "M"]

}

}

}


3.5 匹配多个字段

@Test

public void test() throws IOException {

SearchRequest request = new SearchRequest("bank");


SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

searchSourceBuilder.query(QueryBuilders.multiMatchQuery("mill Lee", "address", "firstname"));


request.source(searchSourceBuilder);


SearchResponse response = client.search(request, RequestOptions.DEFAULT);


SearchHits hits = response.getHits();

System.out.println("total: " + hits.getTotalHits());

for (SearchHit hit : hits) {

Map<String, Object> map = hit.getSourceAsMap();

System.out.println(map);

}

}

等价的命令

GET bank/_search

{

"query": {

"multi_match": {

"query": "mill Lee",

"fields": ["address", "firstname"]

}

}

}


3.6 bool查询

@Test

public void test() throws IOException {

SearchRequest request = new SearchRequest("bank");


SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()

.must(QueryBuilders.matchQuery("address", "mill"))

.must(QueryBuilders.matchQuery("gender", "M"));


searchSourceBuilder.query(boolQueryBuilder);

request.source(searchSourceBuilder);


SearchResponse response = client.search(request, RequestOptions.DEFAULT);

SearchHits hits = response.getHits();

System.out.println("total: " + hits.getTotalHits());

for (SearchHit hit : hits) {

Map<String, Object> map = hit.getSourceAsMap();

System.out.println(map);

}

}

等价命令

GET bank/_search

{

"query": {

"bool": {

"must": [

{

"match": {

"address": "mill"

}

},

{

"match": {

"gender": "M"

}

}

]

}

}

}



3.7 高亮

先完成以下查询,此时不添加高亮效果:

@Test

public void test() throws IOException {

SearchRequest request = new SearchRequest("bank");


SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()

.must(QueryBuilders.matchQuery("address", "mill"));


searchSourceBuilder.query(boolQueryBuilder);

request.source(searchSourceBuilder);


SearchResponse response = client.search(request, RequestOptions.DEFAULT);

SearchHits hits = response.getHits();

System.out.println("total: " + hits.getTotalHits());

for (SearchHit hit : hits) {

Map<String, Object> map = hit.getSourceAsMap();

System.out.println(map);

}

}


为查询结果添加高亮效果:

@Test

public void test() throws IOException {

SearchRequest request = new SearchRequest("bank");


SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()

.must(QueryBuilders.matchQuery("address", "mill"));


HighlightBuilder highlightBuilder = new HighlightBuilder();

HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field("address");

// highlightTitle.preTags("<b>");

// highlightTitle.postTags("</b>");

highlightBuilder.field(highlightTitle);

searchSourceBuilder.highlighter(highlightBuilder);


searchSourceBuilder.query(boolQueryBuilder);

request.source(searchSourceBuilder);


SearchResponse response = client.search(request, RequestOptions.DEFAULT);

SearchHits hits = response.getHits();

System.out.println("total: " + hits.getTotalHits());

for (SearchHit hit : hits) {

中包含的数据,不会高亮

Map<String, Object> map = hit.getSourceAsMap();


Map<String, HighlightField> highlightFields = hit.getHighlightFields();

HighlightField highlight = highlightFields.get("address");

if (highlight != null) {

Text[] fragments = highlight.fragments();

String fragmentString = fragments[0].string();

map.put("highlight", fragmentString);

}

System.out.println(map);

}

}


6. SpringBoot集成ElasticSearch_User_02



GET bank/_search

{

"query": {

"match": {

"address": "Lee Avenue"

}

},

"highlight": {

"fields": {

"address": {

"pre_tags": "<font color='red'>",

"post_tags": "</font>"


}

}

}

}


4.0 聚合

@Test

public void test() throws IOException {

SearchRequest request = new SearchRequest("bank");


SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();


检索,address中包含mill的记录

searchSourceBuilder.query(QueryBuilders.matchQuery("address", "mill"));


聚合,计算结果中,每个年龄各多少个账户

TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age");

searchSourceBuilder.aggregation(ageAgg);


聚合,计算结果中,所有账户的平均余额

AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("balanceAvg").field("balance");

searchSourceBuilder.aggregation(balanceAvg);


request.source(searchSourceBuilder);


检索条件:" + searchSourceBuilder.toString());


SearchResponse response = client.search(request, RequestOptions.DEFAULT);


获取数据

检索结果:");

SearchHits hits = response.getHits();

for (SearchHit hit : hits) {

Map<String, Object> map = hit.getSourceAsMap();

System.out.println(map);

}


聚合结果:");

获取聚合结果

Aggregations aggregations = response.getAggregations();


Terms aggregation = aggregations.get("ageAgg");

List<? extends Terms.Bucket> buckets = aggregation.getBuckets();

buckets.forEach(bucket -> {

String key = bucket.getKeyAsString();

年龄:" + key + " --- " + bucket.getDocCount());

});


Avg aggregation2 = aggregations.get("balanceAvg");

平均薪资:" + aggregation2.getValue());

}


运行结果:

6. SpringBoot集成ElasticSearch_User_03




可以进入以下网址:

https://www.bejson.com/json2javapojo/new/


根据json生成对应的JavaBean

6. SpringBoot集成ElasticSearch_System_04



生成的JavaBean

6. SpringBoot集成ElasticSearch_User_05



进而,可以使用该JavaBean封装json数据。再响应给前端。