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);
}
}
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);
}
}
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());
}
运行结果:
可以进入以下网址:
https://www.bejson.com/json2javapojo/new/
根据json生成对应的JavaBean
生成的JavaBean
进而,可以使用该JavaBean封装json数据。再响应给前端。