SpringBoot2.5.6整合Elasticsearch7.12.1
下面将通过SpringBoot
整合Elasticseach
,SpringBoot的版本是2.5.6
,Elasticsearch的版本是7.12.1
。
SpringBoot整合Elasticsearch主要有三种方式,一种是通过elasticsearch-rest-high-level-client
,另一
种是通过spring-boot-starter-data-elasticsearch
,最后一种是通过transport
。
RestHighLevelClient
更强大,更灵活,但是不能友好的操作对象,ElasticSearchRepository
对象操作友
好。
官方文档:
https://docs.spring.io/spring-data/elasticsearch/docs/4.0.1.RELEASE/reference/html/#preface
https://spring.io/projects/spring-data-elasticsearch/#learn
https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.2/java-docs.html
1、elasticsearch-rest-high-level-client方式
使用RestHighLevelClient
操作
官网地址:
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high.html
1.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-elasticsearch1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-elasticsearch1</name>
<description>spring-boot-elasticsearch1</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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.2 添加配置文件EsConfig
package com.example.springbootelasticsearch1.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author zhangshixing
* @date 2021年11月09日 21:57
*/
@Configuration
public class EsConfig {
@Value("${elasticsearch.hostname}")
private String hostname;
@Value("${elasticsearch.port}")
private int port;
/**
* HighLevelRestConfig
*/
@Bean
public RestHighLevelClient restHighLevelClient() {
// 如果有多个从节点可以持续在内部new多个HttpHost,参数1是IP,参数2是端口,参数3是通信协议
return new RestHighLevelClient(RestClient.builder(new HttpHost(hostname, port, "http")));
}
}
1.3 配置yml文件
# es配置
elasticsearch.hostname=127.0.0.1
elasticsearch.port=9200
# 默认配置,在本地启动的时候可以不配置
# spring.elasticsearch.rest.uris=http://127.0.0.1:9200
1.4 service
package com.example.springbootelasticsearch1.service;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import java.io.IOException;
/**
* @author zhangshixing
* @date 2021年11月09日 22:05
*/
public interface IRestHighLevelClientService {
// 创建索引
CreateIndexResponse createIndex() throws IOException;
// 删除索引
AcknowledgedResponse deleteIndex() throws IOException;
// 查看索引是否存在
boolean existIndex() throws IOException;
// 更新索引的settings配置
AcknowledgedResponse updateIndexSettings() throws IOException;
// 更新索引的mapping配置
AcknowledgedResponse updateIndexMapping() throws IOException;
// 新增文档
IndexResponse addDocument() throws IOException;
// 修改文档
UpdateResponse updateDocument() throws IOException;
// 根据id删除文档
DeleteResponse deleteDocumentById() throws IOException;
// 根据条件删除文档
BulkByScrollResponse deleteDocumentByCon() throws IOException;
// 批量操作文档
BulkResponse bulkDocument() throws IOException;
// 查询操作
SearchResponse searchDocument1() throws IOException;
// 查询操作2
SearchResponse searchDocument2() throws IOException;
// 高亮查询
SearchResponse searchDocument3() throws IOException;
}
1.5 serviceImpl
package com.example.springbootelasticsearch1.service.impl;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
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.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.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.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.AvgAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author zhangshixing
* @date 2021年11月09日 22:06
*/
@Service
public class RestHighLevelClientServiceImpl implements IRestHighLevelClientService {
// 引入RestHighLevelClient
@Autowired
private RestHighLevelClient restHighLevelClient;
// 新建索引
@Override
public CreateIndexResponse createIndex() throws IOException {
String indexName = "student";
CreateIndexRequest request = new CreateIndexRequest(indexName.toLowerCase());
request.settings(Settings.builder()
.put("index.number_of_shards", 5)
.put("index.number_of_replicas", 0)
);
// mapping部分,除了用json字符串来定义外,还可以使用Map或者XContentBuilder
// 这里使用XContentBuilder
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.startObject("properties");
{
builder.startObject("id");
{
builder.field("type", "integer");
}
builder.endObject();
builder.startObject("name");
{
builder.field("type", "text");
}
builder.endObject();
builder.startObject("age");
{
builder.field("type", "integer");
}
builder.endObject();
builder.startObject("description");
{
builder.field("type", "text");
builder.field("analyzer", "ik_max_word");
}
builder.endObject();
builder.startObject("birthday");
{
builder.field("type", "date");
}
builder.endObject();
}
builder.endObject();
}
builder.endObject();
request.mapping(builder);
// 同步的方式执行
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
// 异步的方式执行
restHighLevelClient.indices().createAsync(request, RequestOptions.DEFAULT, new ActionListener<CreateIndexResponse>() {
@Override
public void onResponse(CreateIndexResponse createIndexResponse1) {
System.out.println("执行情况:" + createIndexResponse1);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
return createIndexResponse;
}
// 删除索引
@Override
public AcknowledgedResponse deleteIndex() throws IOException {
String indexName = "student";
DeleteIndexRequest indexRequest = new DeleteIndexRequest(indexName);
// 同步执行
AcknowledgedResponse delete = restHighLevelClient.indices().delete(indexRequest, RequestOptions.DEFAULT);
// 异步执行
/*
restHighLevelClient.indices().deleteAsync(indexRequest, RequestOptions.DEFAULT, new ActionListener<AcknowledgedResponse>() {
@Override
public void onResponse(AcknowledgedResponse acknowledgedResponse) {
System.out.println("执行情况:" + acknowledgedResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return delete;
}
// 查看索引是否存在
@Override
public boolean existIndex() throws IOException {
String indexName = "student";
GetIndexRequest request = new GetIndexRequest(indexName);
// 同步执行
boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
// 异步执行
/*
restHighLevelClient.indices().existsAsync(request, RequestOptions.DEFAULT, new ActionListener<Boolean>() {
@Override
public void onResponse(Boolean aBoolean) {
System.out.println("执行情况:" + aBoolean);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return exists;
}
// 更新索引的settings配置
@Override
public AcknowledgedResponse updateIndexSettings() throws IOException {
String indexName = "student";
UpdateSettingsRequest request = new UpdateSettingsRequest(indexName);
String settingKey = "index.number_of_replicas";
int settingValue = 2;
Settings.Builder settingsBuilder = Settings.builder().put(settingKey, settingValue);
request.settings(settingsBuilder);
// 是否更新已经存在的settings配置默认false
request.setPreserveExisting(true);
// 更新settings配置(同步)
AcknowledgedResponse updateSettingsResponse = restHighLevelClient.indices().putSettings(request, RequestOptions.DEFAULT);
// 更新settings配置(异步)
/*
restHighLevelClient.indices().putSettingsAsync(request, RequestOptions.DEFAULT, new ActionListener<AcknowledgedResponse>() {
@Override
public void onResponse(AcknowledgedResponse acknowledgedResponse) {
System.out.println("执行情况:" + acknowledgedResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return updateSettingsResponse;
}
// 更新索引的mapping配置
@Override
public AcknowledgedResponse updateIndexMapping() throws IOException {
String indexName = "student";
PutMappingRequest request = new PutMappingRequest(indexName);
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.startObject("properties");
{
// 会在以前索引的基础上新增sex字段
builder.startObject("sex");
{
builder.field("type", "integer");
}
builder.endObject();
}
builder.endObject();
}
builder.endObject();
request.source(builder);
// 新增mapping配置(同步)
AcknowledgedResponse putMappingResponse = restHighLevelClient.indices().putMapping(request, RequestOptions.DEFAULT);
// 新增mapping配置(异步)
/*
restHighLevelClient.indices().putMappingAsync(request, RequestOptions.DEFAULT, new ActionListener<AcknowledgedResponse>() {
@Override
public void onResponse(AcknowledgedResponse acknowledgedResponse) {
System.out.println("执行情况:" + acknowledgedResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return putMappingResponse;
}
// 新增文档
@Override
public IndexResponse addDocument() throws IOException {
String indexName = "student";
IndexRequest request = new IndexRequest(indexName);
// id为1的数据
request.id("1");
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("id", 1);
jsonMap.put("name", "tom");
jsonMap.put("age", 24);
jsonMap.put("description", "tom是一个好学生");
jsonMap.put("birthday", new Date());
jsonMap.put("sex", 1);
request.source(jsonMap);
request.routing("routing");
// 同步方式
IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
// 异步方式
/*
restHighLevelClient.indexAsync(request, RequestOptions.DEFAULT, new ActionListener<IndexResponse>() {
@Override
public void onResponse(IndexResponse indexResponse) {
System.out.println("执行情况: " + indexResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return indexResponse;
}
// 修改文档
@Override
public UpdateResponse updateDocument() throws IOException {
String indexName = "student";
// 传入索引名称和需要更新的Document的id
UpdateRequest request = new UpdateRequest(indexName, "1");
// 更新的内容会与数据本身合并,若存在则更新,不存在则新增
// 组装更新内容的数据结构有四种: json字符串、Map、XContentBuilder、Key-Value
// json字符串
/*
String jsonString = "{" +
"\"updated\":\"2020-03-29\"," +
"\"reason\":\"daily update\"" +
"}";
request.doc(jsonString);
*/
// Map
/*
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("updated", new Date());
jsonMap.put("reason", "daily update");
request.doc(jsonMap);
*/
// XContentBuilder
/*
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
builder.timeField("updated", new Date());
builder.timeField("reason", "daily update");
builder.endObject();
request.doc(builder);
*/
// Key-Value,可以包含多个键值对
request.doc("description", "tom是一个好学生,考上大学肯定没有问题!");
// 同步的方式发送更新请求
UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);
// 异步方式
/*
restHighLevelClient.updateAsync(request, RequestOptions.DEFAULT, new ActionListener<UpdateResponse>() {
@Override
public void onResponse(UpdateResponse updateResponse) {
System.out.println("执行情况: " + updateResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return updateResponse;
}
// 根据id删除文档
@Override
public DeleteResponse deleteDocumentById() throws IOException {
String indexName = "student";
DeleteRequest deleteRequest = new DeleteRequest(indexName, "1");
// 同步方式
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
// 异步方式
/*
restHighLevelClient.deleteAsync(deleteRequest, RequestOptions.DEFAULT, new ActionListener<DeleteResponse>() {
@Override
public void onResponse(DeleteResponse deleteResponse) {
System.out.println("执行情况: " + deleteResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return deleteResponse;
}
// 根据条件删除文档
@Override
public BulkByScrollResponse deleteDocumentByCon() throws IOException {
String indexName = "student";
DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(indexName);
// 待删除的数据需要满足的条件
deleteByQueryRequest.setQuery(new TermQueryBuilder("name", "tom"));
// 忽略版本冲突
deleteByQueryRequest.setConflicts("proceed");
// 同步的方式删除
BulkByScrollResponse deleteResponse = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
// 异步的方式
/*
restHighLevelClient.deleteByQueryAsync(deleteByQueryRequest, RequestOptions.DEFAULT, new ActionListener<BulkByScrollResponse>() {
@Override
public void onResponse(BulkByScrollResponse bulkByScrollResponse) {
System.out.println("执行情况: " + deleteResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return deleteResponse;
}
// 批量操作文档
@Override
public BulkResponse bulkDocument() throws IOException {
String indexName = "student";
BulkRequest request = new BulkRequest();
// 普通的PUT操作,相当于全量替换或新增
request.add(new IndexRequest(indexName).id("2").source(XContentType.JSON, "name", "zsx", "age", "25"));
// 更新操作
request.add(new UpdateRequest(indexName, "2").doc(XContentType.JSON, "sex", 1));
// 删除操作
request.add(new DeleteRequest(indexName, "2"));
// 同步操作
BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
boolean hasFailures = bulkResponse.hasFailures();
System.out.println("批量操作是否失败:" + hasFailures);
BulkItemResponse[] items = bulkResponse.getItems();
for (BulkItemResponse item : items) {
System.out.println(item.status());
}
// 异步操作
/*
restHighLevelClient.bulkAsync(request, RequestOptions.DEFAULT, new ActionListener<BulkResponse>() {
@Override
public void onResponse(BulkResponse bulkItemResponses) {
System.out.println("执行情况: " + bulkItemResponses);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return bulkResponse;
}
// 查询操作1
@Override
public SearchResponse searchDocument1() throws IOException {
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
BoolQueryBuilder booleanQueryBuilder = QueryBuilders.boolQuery();
// 过滤出年龄在15~40岁之间的document
booleanQueryBuilder.filter(QueryBuilders.rangeQuery("age").from(15).to(40));
// bool must条件, 找出description字段中包含学生的document
booleanQueryBuilder.must(QueryBuilders.matchQuery("description", "学生"));
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 执行查询条件
// sourceBuilder.query(QueryBuilders.matchAllQuery());
sourceBuilder.query(booleanQueryBuilder);
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "tom");
sourceBuilder.query(matchQueryBuilder);
//聚合年龄分布
TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age");
sourceBuilder.aggregation(ageAgg);
//聚合平均年龄
AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("ageAvg").field("age");
sourceBuilder.aggregation(balanceAvg);
// 分页查询
sourceBuilder.from(0);
sourceBuilder.size(5);
// 排序
sourceBuilder.sort("age", SortOrder.DESC);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(sourceBuilder);
// 同步的方式发送请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String hitString = hit.getSourceAsString();
System.out.println(hitString);
}
// 异步方式发送请求
/*
restHighLevelClient.searchAsync(searchRequest, RequestOptions.DEFAULT, new ActionListener<SearchResponse>() {
@Override
public void onResponse(SearchResponse searchResponse) {
System.out.println("执行情况: " + searchResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return searchResponse;
}
// 查询操作2
@Override
public SearchResponse searchDocument2() throws IOException {
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
//构建搜索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
// 在student索引的description和name字段中都查询“tom”
.query(QueryBuilders.multiMatchQuery("tom", "description", "name"))
// matchQuery是模糊查询,会对key进行分词
// searchSourceBuilder.query(QueryBuilders.matchQuery(key,value));
// termQuery是精准查询
// searchSourceBuilder.query(QueryBuilders.termQuery(key,value));
.sort(SortBuilders.fieldSort("age").order(SortOrder.DESC))
// 一个可选项,用于控制允许搜索的时间
// searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 指定从哪条开始查询
.from(0)
// 需要查出的总记录条数
.size(10);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String hitString = hit.getSourceAsString();
System.out.println(hitString);
}
// 异步方式发送请求
/*
restHighLevelClient.searchAsync(searchRequest, RequestOptions.DEFAULT, new ActionListener<SearchResponse>() {
@Override
public void onResponse(SearchResponse searchResponse) {
System.out.println("执行情况: " + searchResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return searchResponse;
}
// 高亮查询
@Override
public SearchResponse searchDocument3() throws IOException {
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
// 高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("name");
highlightBuilder.field("description");
highlightBuilder.requireFieldMatch(false);
highlightBuilder.preTags("<span style='color:red'>");
highlightBuilder.postTags("</span>");
// 构建搜索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
.query(QueryBuilders.multiMatchQuery("tom", "description", "name"))
.sort(SortBuilders.fieldSort("age").order(SortOrder.DESC))
// 指定从哪条开始查询
.from(0)
// 需要查出的总记录条数
.size(10)
//高亮
.highlighter(highlightBuilder);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String hitString = hit.getSourceAsString();
System.out.println(hitString);
// 处理高亮显示的结果
HighlightField titleField = hit.getHighlightFields().get("name");
if (titleField != null) {
// 新建一个对象,把该属性的值重新覆盖
System.out.println(titleField.getFragments()[0].toString());
}
HighlightField contentField = hit.getHighlightFields().get("description");
if (contentField != null) {
System.out.println(contentField.getFragments()[0].toString());
}
}
// 异步方式发送请求
/*
restHighLevelClient.searchAsync(searchRequest, RequestOptions.DEFAULT, new ActionListener<SearchResponse>() {
@Override
public void onResponse(SearchResponse searchResponse) {
System.out.println("执行情况: " + searchResponse);
}
@Override
public void onFailure(Exception e) {
System.out.println("执行失败的原因:" + e.getMessage());
}
});
*/
return searchResponse;
}
}
1.6 测试类
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 新建索引
@Test
void createIndex() throws IOException {
CreateIndexResponse createIndexResponse = iRestHighLevelClientService.createIndex();
System.out.println("新建的索引是:" + createIndexResponse.index());
}
// 删除索引
@Test
void deleteIndex() throws IOException {
AcknowledgedResponse acknowledgedResponse = iRestHighLevelClientService.deleteIndex();
System.out.println("删除索引是否成功:" + acknowledgedResponse.isAcknowledged());
}
// 查看索引是否存在
@Test
void existIndex() throws IOException {
Boolean aBoolean = iRestHighLevelClientService.existIndex();
System.out.println("索引是否存在:" + aBoolean);
}
// 更新索引的settings配置
@Test
void updateIndexSettings() throws IOException {
AcknowledgedResponse acknowledgedResponse = iRestHighLevelClientService.updateIndexSettings();
System.out.println("是否更新settings配置成功:" + acknowledgedResponse.isAcknowledged());
}
// 更新索引的mapping配置
@Test
void updateIndexMapping() throws IOException {
AcknowledgedResponse acknowledgedResponse = iRestHighLevelClientService.updateIndexMapping();
System.out.println("是否更新mapping配置成功:" + acknowledgedResponse.isAcknowledged());
}
// 新增文档
@Test
void addDocument() throws IOException {
IndexResponse indexResponse = iRestHighLevelClientService.addDocument();
System.out.println("新增文档是否成功:" + indexResponse.status());
}
// 修改文档
@Test
void addDocumentByCon() throws IOException {
UpdateResponse updateResponse = iRestHighLevelClientService.updateDocument();
System.out.println("修改文档是否成功:" + updateResponse.status());
}
// 根据id删除文档
@Test
void deleteDocumentById() throws IOException {
DeleteResponse deleteResponse = iRestHighLevelClientService.deleteDocumentById();
System.out.println("删除文档是否成功:" + deleteResponse.status());
}
// 根据条件删除文档
@Test
void deleteDocumentByCon() throws IOException {
BulkByScrollResponse bulkByScrollResponse = iRestHighLevelClientService.deleteDocumentByCon();
System.out.println("删除文档是否成功:" + bulkByScrollResponse.getDeleted());
}
// 批量操作文档
@Test
void bulkDocument() throws IOException {
BulkResponse bulkItemResponses = iRestHighLevelClientService.bulkDocument();
System.out.println("批量操作文档是否成功:" + bulkItemResponses.status());
}
// 查询操作1
@Test
void searchDocument1() throws IOException {
SearchResponse searchResponse = iRestHighLevelClientService.searchDocument1();
System.out.println("查询数据的总数:" + searchResponse.getHits().getHits().length);
System.out.println("符合条件的文档最大得分: " + searchResponse.getHits().getMaxScore());
}
// 查询操作2
@Test
void searchDocument2() throws IOException {
SearchResponse searchResponse = iRestHighLevelClientService.searchDocument2();
System.out.println("查询数据的总数:" + searchResponse.getHits().getHits().length);
System.out.println("符合条件的文档最大得分: " + searchResponse.getHits().getMaxScore());
}
// 高亮查询
@Test
void searchDocument3() throws IOException {
SearchResponse searchResponse = iRestHighLevelClientService.searchDocument3();
System.out.println("查询数据的总数:" + searchResponse.getHits().getHits().length);
System.out.println("符合条件的文档最大得分: " + searchResponse.getHits().getMaxScore());
}
}
1.7 启动类
package com.example.springbootelasticsearch1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootElasticsearch1Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootElasticsearch1Application.class, args);
}
}
1.8 测试
1.8.1创建索引
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 新建索引
@Test
void createIndex() throws IOException {
CreateIndexResponse createIndexResponse = iRestHighLevelClientService.createIndex();
System.out.println("新建的索引是:" + createIndexResponse.index());
}
}
1.8.2 删除索引
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 删除索引
@Test
void deleteIndex() throws IOException {
AcknowledgedResponse acknowledgedResponse = iRestHighLevelClientService.deleteIndex();
System.out.println("删除索引是否成功:" + acknowledgedResponse.isAcknowledged());
}
}
1.8.3 查看索引是否存在
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 查看索引是否存在
@Test
void existIndex() throws IOException {
Boolean aBoolean = iRestHighLevelClientService.existIndex();
System.out.println("索引是否存在:" + aBoolean);
}
}
我们再次新建索引:
1.8.4 更新索引的settings配置
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 更新索引的settings配置
@Test
void updateIndexSettings() throws IOException {
AcknowledgedResponse acknowledgedResponse = iRestHighLevelClientService.updateIndexSettings();
System.out.println("是否更新settings配置成功:" + acknowledgedResponse.isAcknowledged());
}
}
1.8.5 更新索引的mapping配置
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 更新索引的mapping配置
@Test
void updateIndexMapping() throws IOException {
AcknowledgedResponse acknowledgedResponse = iRestHighLevelClientService.updateIndexMapping();
System.out.println("是否更新mapping配置成功:" + acknowledgedResponse.isAcknowledged());
}
}
1.8.6 新增文档
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.index.IndexResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 新增文档
@Test
void addDocument() throws IOException {
IndexResponse indexResponse = iRestHighLevelClientService.addDocument();
System.out.println("新增文档是否成功:" + indexResponse.status());
}
}
1.8.7 修改文档
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.update.UpdateResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 修改文档
@Test
void addDocumentByCon() throws IOException {
UpdateResponse updateResponse = iRestHighLevelClientService.updateDocument();
System.out.println("修改文档是否成功:" + updateResponse.status());
}
}
1.8.8 根据id删除文档
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.delete.DeleteResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 根据id删除文档
@Test
void deleteDocumentById() throws IOException {
DeleteResponse deleteResponse = iRestHighLevelClientService.deleteDocumentById();
System.out.println("删除文档是否成功:" + deleteResponse.status());
}
}
1.8.9 根据条件删除文档
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 根据条件删除文档
@Test
void deleteDocumentByCon() throws IOException {
BulkByScrollResponse bulkByScrollResponse = iRestHighLevelClientService.deleteDocumentByCon();
System.out.println("删除文档是否成功:" + bulkByScrollResponse.getDeleted());
}
}
新增一条文档,然后再进行测试:
1.8.10 批量操作文档
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.bulk.BulkResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 批量操作文档
@Test
void bulkDocument() throws IOException {
BulkResponse bulkItemResponses = iRestHighLevelClientService.bulkDocument();
System.out.println("批量操作文档是否成功:" + bulkItemResponses.status());
}
}
1.8.11 查询操作1
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.search.SearchResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 查询操作1
@Test
void searchDocument1() throws IOException {
SearchResponse searchResponse = iRestHighLevelClientService.searchDocument1();
System.out.println("查询数据的总数:" + searchResponse.getHits().getHits().length);
System.out.println("符合条件的文档最大得分: " + searchResponse.getHits().getMaxScore());
}
}
1.8.12 查询操作2
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.search.SearchResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 查询操作2
@Test
void searchDocument2() throws IOException {
SearchResponse searchResponse = iRestHighLevelClientService.searchDocument2();
System.out.println("查询数据的总数:" + searchResponse.getHits().getHits().length);
System.out.println("符合条件的文档最大得分: " + searchResponse.getHits().getMaxScore());
}
}
1.8.13 高亮查询
package com.example.springbootelasticsearch1;
import com.example.springbootelasticsearch1.service.IRestHighLevelClientService;
import org.elasticsearch.action.search.SearchResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
@SpringBootTest
class SpringBootElasticsearch1ApplicationTests {
@Autowired
private IRestHighLevelClientService iRestHighLevelClientService;
// 高亮查询
@Test
void searchDocument3() throws IOException {
SearchResponse searchResponse = iRestHighLevelClientService.searchDocument3();
System.out.println("查询数据的总数:" + searchResponse.getHits().getHits().length);
System.out.println("符合条件的文档最大得分: " + searchResponse.getHits().getMaxScore());
}
}
1.9 自定义查询
-
matchQuery
:词条匹配,先分词然后在调用termQuery进行匹配 -
termQuery
:词条匹配,不分词 -
wildcardQuery
:通配符匹配 -
fuzzyQuery
:模糊匹配 -
rangeQuery
:范围匹配 -
booleanQuery
:布尔查询
match query (词条匹配,先分词然后在调用termQuery进行匹配)
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("title", "小米手机"));
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
termQuery (词条匹配,不分词)
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("title", "小米"));
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
fuzzyQuery (模糊匹配)
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.fuzzyQuery("title", "小米"));
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
booleanQuery (布尔查询)
BooleanClause
用于表示布尔查询子句关系的类,包括:BooleanClause.Occur.MUST
,
BooleanClause.Occur.MUST_NOT
,BooleanClause.Occur.SHOULD
。
必须包含,不能包含,可以包含三种。
有以下6种组合:
1.MUST
和MUST
:交集。
2.MUST
和MUST_NOT
:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。
3.SHOULD
与MUST_NOT
:连用时,功能同MUST和MUST_NOT。
4.SHOULD
与MUST
连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序。
5.SHOULD与SHOULD
:并集。
6.MUST_NOT
和MUST_NOT
:无意义,检索无结果。
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("title", "手机")).must(QueryBuilders.termQuery("brand", "小米")));searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
RangeQuery 范围查找
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.rangeQuery("price").from(3000).to(4000));
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
wildcardQuery 通配符匹配
String indexName = "student";
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.wildcardQuery("title", "%小米%"));
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
2、spring-boot-starter-data-elasticsearch方式
使用ElasticsearchRepository
进行操作,ElasticSearchRepository
方式主要通过注解和对接口实现的方式
来实现ES的操作,我们在实体类上通过注解配置ES索引的映射关系后,当实现了ElasticSearchRepository接口的
类第一次操作ES进行插入文档的时候,ES会自动生成所需要的一切。但是该种方式无法实现高亮查询,想要实现
高亮查询只能使用RestHighLevelClient
。
想要使用高版本,在创建实体的@Document
属性中不可以加入type = "_doc"
2.1 导入依赖
<?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-elasticsearch2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-elasticsearch2</name>
<description>spring-boot-elasticsearch2</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.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 配置类和配置文件
package com.example.springbootelasticsearch2.config;
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 org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
@Configuration
public class EsConfig extends AbstractElasticsearchConfiguration {
@Value("${elasticsearch.hostname}")
private String hostname;
@Value("${elasticsearch.port}")
private int port;
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder().
connectedTo(hostname + ":" + port).build();
return RestClients.create(clientConfiguration).rest();
}
}
# es配置
elasticsearch.hostname=127.0.0.1
elasticsearch.port=9200
# 默认配置,在本地启动的时候可以不配置
# spring.elasticsearch.rest.uris=http://127.0.0.1:9200
2.3 创建实体类
package com.example.springbootelasticsearch2.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.util.Date;
@Document(indexName = "book", createIndex = true)
public class Book {
@Id
@Field(type = FieldType.Text)
private String id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String author;
@Field(type = FieldType.Double)
private Double price;
@Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private Date createTime;
@Field(type = FieldType.Date, format = DateFormat.basic_date_time)
private Date updateTime;
public Book() {
}
public Book(String id, String title, String author, Double price, Date createTime, Date updateTime) {
this.id = id;
this.title = title;
this.author = author;
this.price = price;
this.createTime = createTime;
this.updateTime = updateTime;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
createIndex = true
:无需手动创建Book索引,SpringBoot启动自动创建。
@Document
: 作用在类,标记实体类为文档对象,一般有四个属性:
indexName
:对应索引库名称shards
:分片数量,默认5replicas
:副本数量,默认1type
:用来指定索引类型,7.x以后的版本移除了
@Id
:作用在成员变量,标记一个字段作为id主键,用来将对象中id和ES中_id映射。
@Field
:作用在成员变量,标记为文档的字段,并指定字段映射属性:
type
:字段类型,取值是枚举:FieldType,具体的数据类型有:text、keyword、long、short、
date、integer、object、byte、double、float、half_float、scaled_float
index
:是否索引,布尔类型,默认是true
store
:是否存储,布尔类型,默认是false
analyzer
:分词器名称,用来指定使用哪种分词器
format
:时间格式
fielddata
:聚类的时候使用
@Transient
:默认情况下,存储或检索文档时,所有字段都映射到文档,此注释不包括该字段。
2.4 创建Repository仓库
package com.example.springbootelasticsearch2.repository;
import com.example.springbootelasticsearch2.entity.Book;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface ESBookRepository extends ElasticsearchRepository<Book, String> {
List<Book> findByTitleOrAuthor(String title, String author);
@Highlight(fields = {
@HighlightField(name = "title"),
@HighlightField(name = "author")
})
@Query("{\"match\":{\"title\":\"?0\"}}")
SearchHits<Book> find(String keyword);
}
2.5 Service
package com.example.springbootelasticsearch2.service;
public interface BookService {
}
package com.example.springbootelasticsearch2.service.impl;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import com.example.springbootelasticsearch2.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
@Autowired
private ESBookRepository esBookRepository;
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
}
2.6 测试类
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 创建索引
*/
@Test
public void testCreateIndex() {
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Book.class);
Document mapping = indexOperations.createMapping();
indexOperations.putMapping(mapping);
}
/**
* 删除索引
*/
@Test
public void testDeleteIndex() {
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Book.class);
indexOperations.delete();
}
/**
* 索引是否存在
*/
@Test
public void testExistsIndex() {
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Book.class);
indexOperations.exists();
}
/**
* 新增文档
*/
@Test
void addDocument() {
Book book1 = new Book("1", "《西游记》", "吴承恩", 49.9, new Date(), new Date());
esBookRepository.save(book1);
Book book2 = new Book("2", "《红楼梦》", "曹雪芹", 59.9, new Date(), new Date());
esBookRepository.save(book2);
Book book3 = new Book("3", "《三国演义》", "罗贯中", 39.9, new Date(), new Date());
esBookRepository.save(book3);
Book book4 = new Book("4", "《水浒传》", "施耐庵", 69.9, new Date(), new Date());
esBookRepository.save(book4);
}
/**
* 一次新增多条文档
*/
@Test
public void addManyDocument() {
List<Book> books = new ArrayList<Book>();
Book book1 = new Book("1", "《西游记》", "吴承恩", 49.9, new Date(), new Date());
Book book2 = new Book("2", "《红楼梦》", "曹雪芹", 59.9, new Date(), new Date());
Book book3 = new Book("3", "《三国演义》", "罗贯中", 39.9, new Date(), new Date());
Book book4 = new Book("4", "《水浒传》", "施耐庵", 69.9, new Date(), new Date());
books.add(book1);
books.add(book2);
books.add(book3);
books.add(book4);
esBookRepository.saveAll(books);
}
/**
* 判断某id的文档是否存在
*/
@Test
void documentExist() {
boolean exists = esBookRepository.existsById("2");
System.out.println(exists);
}
/**
* 修改文档
*/
@Test
void updateDocument() {
Book book = new Book("1", "《西游记》", "吴承恩", 149.9, new Date(), new Date());
esBookRepository.save(book);
}
/**
* 删除文档
*/
@Test
void deleteDocument() {
esBookRepository.deleteById("1");
}
/**
* 删除所有文档
*/
@Test
void deleteAllDocument() {
esBookRepository.deleteAll();
}
/**
* 根据id查询文档
*/
@Test
void queryDocumentByID() {
Optional<Book> book = esBookRepository.findById("1");
System.out.println(book.get());
}
/**
* 查询所有文档
*/
@Test
void queryAllDocument() {
Iterable<Book> all = esBookRepository.findAll();
all.forEach(System.out::println);
}
/**
* 排序文档
*/
@Test
void sortAllDocument() {
Iterable<Book> all = esBookRepository.findAll(Sort.by(Sort.Order.asc("price")));
all.forEach(System.out::println);
}
// 分页
@Test
void pageDocument() {
// SpringBoot2.5.6已经移除了ElasticsearchRepository里的search()方法,只剩了一些特别基础的增删改查,基本上是不能用的。
// 官方明显是想让开发者用ElasticsearchRestTemplate去做。
MatchAllQueryBuilder matchQueryBuilder = QueryBuilders.matchAllQuery();
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(matchQueryBuilder);
nativeSearchQuery.setPageable(PageRequest.of(2, 1));
SearchHits<Book> search = elasticsearchRestTemplate.search(nativeSearchQuery, Book.class);
for (SearchHit<Book> hit : search.getSearchHits()) {
System.out.println(hit.getContent().getTitle());
}
}
// 自定义查询
@Test
void query1() {
List<Book> book = esBookRepository.findByTitleOrAuthor("《红楼梦》", "曹雪芹");
book.forEach(System.out::println);
}
// 自定义json规则查询
@Test
void query2() {
SearchHits<Book> book = esBookRepository.find("《红楼梦》");
// SearchHit{id='2', score=3.1789374, sortValues=[], content=com.example.springbootelasticsearch2.entity.Book@48cb2d73,
// highlightFields={title=[《<em>红楼梦</em>》]}}
book.forEach(System.out::println);
}
}
2.7 启动类
package com.example.springbootelasticsearch2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootElasticsearch2Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootElasticsearch2Application.class, args);
}
}
2.8 测试
2.8.1 新建索引
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 创建索引
*/
@Test
public void testCreateIndex() {
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Book.class);
Document mapping = indexOperations.createMapping();
indexOperations.putMapping(mapping);
}
}
2.8.2 删除索引
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 删除索引
*/
@Test
public void testDeleteIndex() {
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Book.class);
indexOperations.delete();
}
}
2.8.3 索引是否存在
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 索引是否存在
*/
@Test
public void testExistsIndex() {
IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(Book.class);
indexOperations.exists();
}
}
2.8.4 新增文档
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import java.util.Date;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 新增文档
*/
@Test
void addDocument() {
Book book1 = new Book("1", "《西游记》", "吴承恩", 49.9, new Date(), new Date());
esBookRepository.save(book1);
Book book2 = new Book("2", "《红楼梦》", "曹雪芹", 59.9, new Date(), new Date());
esBookRepository.save(book2);
Book book3 = new Book("3", "《三国演义》", "罗贯中", 39.9, new Date(), new Date());
esBookRepository.save(book3);
Book book4 = new Book("4", "《水浒传》", "施耐庵", 69.9, new Date(), new Date());
esBookRepository.save(book4);
}
}
2.8.5 判断某id的文档是否存在
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 判断某id的文档是否存在
*/
@Test
void documentExist() {
boolean exists = esBookRepository.existsById("2");
System.out.println(exists);
}
}
2.8.6 修改文档
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import java.util.Date;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 修改文档
*/
@Test
void updateDocument() {
Book book = new Book("1", "《西游记》", "吴承恩", 149.9, new Date(), new Date());
esBookRepository.save(book);
}
}
2.8.7 删除文档
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 删除文档
*/
@Test
void deleteDocument() {
esBookRepository.deleteById("1");
}
}
2.8.8 刪除所有文档
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 删除所有文档
*/
@Test
void deleteAllDocument() {
esBookRepository.deleteAll();
}
}
2.8.9 一次新增多条文档
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 一次新增多条文档
*/
@Test
public void addManyDocument() {
List<Book> books = new ArrayList<Book>();
Book book1 = new Book("1", "《西游记》", "吴承恩", 49.9, new Date(), new Date());
Book book2 = new Book("2", "《红楼梦》", "曹雪芹", 59.9, new Date(), new Date());
Book book3 = new Book("3", "《三国演义》", "罗贯中", 39.9, new Date(), new Date());
Book book4 = new Book("4", "《水浒传》", "施耐庵", 69.9, new Date(), new Date());
books.add(book1);
books.add(book2);
books.add(book3);
books.add(book4);
esBookRepository.saveAll(books);
}
}
2.8.10 根据id查询文档
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import java.util.Optional;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 根据id查询文档
*/
@Test
void queryDocumentByID() {
Optional<Book> book = esBookRepository.findById("2");
System.out.println(book.get());
}
}
2.8.11 查询所有文档
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import java.util.Optional;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 查询所有文档
*/
@Test
void queryAllDocument() {
Iterable<Book> all = esBookRepository.findAll();
all.forEach(System.out::println);
}
}
2.8.12 排序文档
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
/**
* 排序文档
*/
@Test
void sortAllDocument() {
Iterable<Book> all = esBookRepository.findAll(Sort.by(Sort.Order.asc("price")));
all.forEach(System.out::println);
}
}
2.8.13 分页
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
// 分页
@Test
void pageDocument() {
// SpringBoot2.5.6已经移除了ElasticsearchRepository里的search()方法,只剩了一些特别基础的增删改查,基本上是不能用的。
// 官方明显是想让开发者用ElasticsearchRestTemplate去做。
MatchAllQueryBuilder matchQueryBuilder = QueryBuilders.matchAllQuery();
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(matchQueryBuilder);
nativeSearchQuery.setPageable(PageRequest.of(2, 1));
SearchHits<Book> search = elasticsearchRestTemplate.search(nativeSearchQuery, Book.class);
for (SearchHit<Book> hit : search.getSearchHits()) {
System.out.println(hit.getContent().getTitle());
}
}
}
2.8.14 自定义查询
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import java.util.List;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
// 自定义查询
@Test
void query1() {
List<Book> book = esBookRepository.findByTitleOrAuthor("《红楼梦》", "曹雪芹");
book.forEach(System.out::println);
}
}
2.8.15 自定义json规则查询
package com.example.springbootelasticsearch2;
import com.example.springbootelasticsearch2.entity.Book;
import com.example.springbootelasticsearch2.repository.ESBookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
@SpringBootTest
class SpringBootElasticsearch2ApplicationTests {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private ESBookRepository esBookRepository;
// 自定义json规则查询
@Test
void query2() {
SearchHits<Book> book = esBookRepository.find("《红楼梦》");
// SearchHit{id='2', score=3.1789374, sortValues=[], content=com.example.springbootelasticsearch2.entity.Book@48cb2d73,
// highlightFields={title=[《<em>红楼梦</em>》]}}
book.forEach(System.out::println);
}
}
2.9 自定义查询规则
Keyword | Sample | Elasticsearch Query String |
---|---|---|
And | findByNameAndPrice | {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Or | findByNameOrPrice | {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Is | findByName | {"bool" : {"must" : {"field" : {"name" : "?"}}}} |
Not | findByNameNot | {"bool" : {"must_not" : {"field" : {"name" : "?"}}}} |
Between | findByPriceBetween | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
LessThanEqual | findByPriceLessThan | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
GreaterThanEqual | findByPriceGreaterThan | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Before | findByPriceBefore | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
After | findByPriceAfter | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Like | findByNameLike | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
StartingWith | findByNameStartingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
EndingWith | findByNameEndingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} |
Contains/Containing | findByNameContaining | {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}} |
In | findByNameIn (Collection<String>names) | {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
NotIn | findByNameNotIn (Collection<String>names) | {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
Near | findByStoreNear | Not Supported Yet ! |
True | findByAvailableTrue | {"bool" : {"must" : {"field" : {"available" : true}}}} |
False | findByAvailableFalse | {"bool" : {"must" : {"field" : {"available" : false}}}} |
OrderBy | findByAvailable TrueOrderByNameDesc | {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}} |
我们只要按照上面的定义在接口中定义相应的方法,无须写实现就可实现我们想要的功能。
系统提供的查询方法中findBy
是一个固定写法,像上面我们定义的方法findByTitle
,其中Title
是我们实体
类中的属性名,这个必须对应上。findByTitle
是下面这样定义的:
{"bool" : {"must" : {"field" : {"title" : "?"}}}}
假如我们现在有个需求需要按照作者查询书籍,我们可以在BookRepository
中定义一个方法,如下:
// 根据作者查询书籍
List<User> findByAuthor(String author);
那么我们可以使用该方法:
@Test
public void testFindBookByAuthor(){
List<Book> bookList = bookRepository.findByAuthor("曹雪芹");
bookList.forEach(System.out::println);
}
其实就是框架底层直接使用下面的命令帮我们实现的查询:
GET /book/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"author":"曹雪芹"
}
}
]
}
}
}
ElasticSearchRepository
实现不了高亮查询,想要实现高亮查询还是需要使用RestHighLevelClient
方式。
3、transport方式(7.x开始弃用)
使用TransportClient
进行操作
3.1 导入依赖
<?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-elasticsearch3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-elasticsearch3</name>
<description>spring-boot-elasticsearch3</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>transport</artifactId>
<version>7.12.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.2 配置文件
elasticsearch.cluster-name = elasticsearch
elasticsearch.ip = 127.0.0.1
elasticsearch.port = 9300
elasticsearch.pool = 5
3.3 创建配置类
package com.example.springbootelasticsearch3.config;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.InetAddress;
/**
* @author zhangshixing
* @date 2021年11月11日 12:28
*/
@Slf4j
@Configuration
public class ElasticSearchConfig {
@Value("${elasticsearch.ip}")
private String hostName;
/**
* 端口
*/
@Value("${elasticsearch.port}")
private String port;
/**
* 集群名称
*/
@Value("${elasticsearch.cluster-name}")
private String clusterName;
/**
* 连接池
*/
@Value("${elasticsearch.pool}")
private String poolSize;
/**
* Bean name default 函数名字
*
* @return
*/
@Bean(name = "transportClient")
public TransportClient transportClient() {
log.info("Elasticsearch初始化开始。。。。。");
TransportClient transportClient = null;
try {
// 配置信息
Settings esSetting = Settings.builder()
//集群名字
.put("cluster.name", clusterName)
//增加嗅探机制,找到ES集群
.put("client.transport.sniff", true)
//增加线程池个数,暂时设为5
.put("thread_pool.search.size", Integer.parseInt(poolSize))
.build();
//配置信息Settings自定义
transportClient = new PreBuiltTransportClient(esSetting);
TransportAddress transportAddress = new TransportAddress(InetAddress.getByName(hostName), Integer.valueOf(port));
transportClient.addTransportAddresses(transportAddress);
} catch (Exception e) {
log.error("elasticsearch TransportClient create error!!", e);
}
return transportClient;
}
}
3.4 创建工具类
package com.example.springbootelasticsearch3.utils;
import java.util.List;
import java.util.Map;
/**
* @author zhangshixing
* @date 2021年11月11日 12:42
*/
public class EsPage {
/**
* 当前页
*/
private int currentPage;
/**
* 每页显示多少条
*/
private int pageSize;
/**
* 总记录数
*/
private int recordCount;
/**
* 本页的数据列表
*/
private List<Map<String, Object>> recordList;
/**
* 总页数
*/
private int pageCount;
/**
* 页码列表的开始索引(包含)
*/
private int beginPageIndex;
/**
* 页码列表的结束索引(包含)
*/
private int endPageIndex;
/**
* 只接受前4个必要的属性,会自动的计算出其他3个属性的值
*
* @param currentPage
* @param pageSize
* @param recordCount
* @param recordList
*/
public EsPage(int currentPage, int pageSize, int recordCount, List<Map<String, Object>> recordList) {
this.currentPage = currentPage;
this.pageSize = pageSize;
this.recordCount = recordCount;
this.recordList = recordList;
// 计算总页码
pageCount = (recordCount + pageSize - 1) / pageSize;
// 计算 beginPageIndex 和 endPageIndex
// >> 总页数不多于10页,则全部显示
if (pageCount <= 10) {
beginPageIndex = 1;
endPageIndex = pageCount;
}
// >> 总页数多于10页,则显示当前页附近的共10个页码
else {
// 当前页附近的共10个页码(前4个 + 当前页 + 后5个)
beginPageIndex = currentPage - 4;
endPageIndex = currentPage + 5;
// 当前面的页码不足4个时,则显示前10个页码
if (beginPageIndex < 1) {
beginPageIndex = 1;
endPageIndex = 10;
}
// 当后面的页码不足5个时,则显示后10个页码
if (endPageIndex > pageCount) {
endPageIndex = pageCount;
beginPageIndex = pageCount - 10 + 1;
}
}
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getRecordCount() {
return recordCount;
}
public void setRecordCount(int recordCount) {
this.recordCount = recordCount;
}
public List<Map<String, Object>> getRecordList() {
return recordList;
}
public void setRecordList(List<Map<String, Object>> recordList) {
this.recordList = recordList;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public int getBeginPageIndex() {
return beginPageIndex;
}
public void setBeginPageIndex(int beginPageIndex) {
this.beginPageIndex = beginPageIndex;
}
public int getEndPageIndex() {
return endPageIndex;
}
public void setEndPageIndex(int endPageIndex) {
this.endPageIndex = endPageIndex;
}
}
package com.example.springbootelasticsearch3.utils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequestBuilder;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* @author zhangshixing
* @date 2021年11月11日 12:39
*/
@Component
@Slf4j
public class ElasticsearchUtil {
@Autowired
private TransportClient transportClient;
private static TransportClient client;
/**
* @PostContruct是spring框架的注解 spring容器初始化的时候执行该方法
*/
@PostConstruct
public void init() {
client = this.transportClient;
}
/**
* 创建索引
*
* @param index
* @return
*/
public static boolean createIndex(String index) {
if (!isIndexExist(index)) {
log.info("Index is not exits!");
}
CreateIndexResponse indexResponse = client.admin().indices().prepareCreate(index).execute().actionGet();
log.info("执行建立成功?" + indexResponse.isAcknowledged());
return indexResponse.isAcknowledged();
}
/**
* 删除索引
*
* @param index
* @return
*/
public static boolean deleteIndex(String index) {
if (!isIndexExist(index)) {
log.info("Index is not exits!");
}
AcknowledgedResponse dResponse = client.admin().indices().prepareDelete(index).execute().actionGet();
if (dResponse.isAcknowledged()) {
log.info("delete index " + index + " successfully!");
} else {
log.info("Fail to delete index " + index);
}
return dResponse.isAcknowledged();
}
/**
* 判断索引是否存在
*
* @param index
* @return
*/
public static boolean isIndexExist(String index) {
IndicesExistsResponse inExistsResponse = client.admin().indices().exists(new IndicesExistsRequest(index)).actionGet();
if (inExistsResponse.isExists()) {
log.info("Index [" + index + "] is exist!");
} else {
log.info("Index [" + index + "] is not exist!");
}
return inExistsResponse.isExists();
}
/**
* 数据添加,正定ID
*
* @param jsonObject 要增加的数据
* @param index 索引,类似数据库
* @param type 类型,类似表
* @param id 数据ID
* @return
*/
public static String addData(JSONObject jsonObject, String index, String type, String id) {
IndexResponse response = client.prepareIndex(index, type, id).setSource(jsonObject).get();
log.info("addData response status:{},id:{}", response.status().getStatus(), response.getId());
return response.getId();
}
/**
* 数据添加
*
* @param jsonObject 要增加的数据
* @param index 索引,类似数据库
* @param type 类型,类似表
* @return
*/
public static String addData(JSONObject jsonObject, String index, String type) {
return addData(jsonObject, index, type, UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
}
/**
* 通过ID删除数据
*
* @param index 索引,类似数据库
* @param type 类型,类似表
* @param id 数据ID
*/
public static void deleteDataById(String index, String type, String id) {
DeleteResponse response = client.prepareDelete(index, type, id).execute().actionGet();
log.info("deleteDataById response status:{},id:{}", response.status().getStatus(), response.getId());
}
/**
* 通过ID 更新数据
*
* @param jsonObject 要增加的数据
* @param index 索引,类似数据库
* @param type 类型,类似表
* @param id 数据ID
* @return
*/
public static void updateDataById(JSONObject jsonObject, String index, String type, String id) {
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index(index).type(type).id(id).doc(jsonObject);
client.update(updateRequest);
}
/**
* 通过ID获取数据
*
* @param index 索引,类似数据库
* @param type 类型,类似表
* @param id 数据ID
* @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
* @return
*/
public static Map<String, Object> searchDataById(String index, String type, String id, String fields) {
GetRequestBuilder getRequestBuilder = client.prepareGet(index, type, id);
if (StringUtils.isNotEmpty(fields)) {
getRequestBuilder.setFetchSource(fields.split(","), null);
}
GetResponse getResponse = getRequestBuilder.execute().actionGet();
return getResponse.getSource();
}
/**
* 使用分词查询,并分页
*
* @param index 索引名称
* @param type 类型名称,可传入多个type逗号分隔
* @param startPage 当前页
* @param pageSize 每页显示条数
* @param query 查询条件
* @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
* @param sortField 排序字段
* @param highlightField 高亮字段
* @return
*/
public static EsPage searchDataPage(String index, String type, int startPage, int pageSize, QueryBuilder query, String fields, String sortField, String highlightField) {
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index);
if (StringUtils.isNotEmpty(type)) {
searchRequestBuilder.setTypes(type.split(","));
}
searchRequestBuilder.setSearchType(SearchType.QUERY_THEN_FETCH);
// 需要显示的字段,逗号分隔(缺省为全部字段)
if (StringUtils.isNotEmpty(fields)) {
searchRequestBuilder.setFetchSource(fields.split(","), null);
}
//排序字段
if (StringUtils.isNotEmpty(sortField)) {
searchRequestBuilder.addSort(sortField, SortOrder.DESC);
}
// 高亮(xxx=111,aaa=222)
if (StringUtils.isNotEmpty(highlightField)) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
//highlightBuilder.preTags("<span style='color:red' >");//设置前缀
//highlightBuilder.postTags("</span>");//设置后缀
// 设置高亮字段
highlightBuilder.field(highlightField);
searchRequestBuilder.highlighter(highlightBuilder);
}
//searchRequestBuilder.setQuery(QueryBuilders.matchAllQuery());
searchRequestBuilder.setQuery(query);
// 分页应用
searchRequestBuilder.setFrom(startPage).setSize(pageSize);
// 设置是否按查询匹配度排序
searchRequestBuilder.setExplain(true);
//打印的内容 可以在 Elasticsearch head 和 Kibana 上执行查询
log.info("\n{}", searchRequestBuilder);
// 执行搜索,返回搜索响应信息
SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
TotalHits totalHits = searchResponse.getHits().getTotalHits();
long length = searchResponse.getHits().getHits().length;
log.debug("共查询到[{}]条数据,处理数据条数[{}]", totalHits.value, length);
if (searchResponse.status().getStatus() == 200) {
// 解析对象
List<Map<String, Object>> sourceList = setSearchResponse(searchResponse, highlightField);
return new EsPage(startPage, pageSize, (int) totalHits.value, sourceList);
}
return null;
}
/**
* 使用分词查询
*
* @param index 索引名称
* @param type 类型名称,可传入多个type逗号分隔
* @param query 查询条件
* @param size 文档大小限制
* @param fields 需要显示的字段,逗号分隔(缺省为全部字段)
* @param sortField 排序字段
* @param highlightField 高亮字段
* @return
*/
public static List<Map<String, Object>> searchListData(String index, String type, QueryBuilder query, Integer size, String fields, String sortField, String highlightField) {
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index);
if (StringUtils.isNotEmpty(type)) {
searchRequestBuilder.setTypes(type.split(","));
}
if (StringUtils.isNotEmpty(highlightField)) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
// 设置高亮字段
highlightBuilder.field(highlightField);
searchRequestBuilder.highlighter(highlightBuilder);
}
searchRequestBuilder.setQuery(query);
if (StringUtils.isNotEmpty(fields)) {
searchRequestBuilder.setFetchSource(fields.split(","), null);
}
searchRequestBuilder.setFetchSource(true);
if (StringUtils.isNotEmpty(sortField)) {
searchRequestBuilder.addSort(sortField, SortOrder.DESC);
}
if (size != null && size > 0) {
searchRequestBuilder.setSize(size);
}
//打印的内容 可以在 Elasticsearch head 和 Kibana 上执行查询
log.info("\n{}", searchRequestBuilder);
SearchResponse searchResponse = searchRequestBuilder.execute().actionGet();
TotalHits totalHits = searchResponse.getHits().getTotalHits();
long length = searchResponse.getHits().getHits().length;
log.info("共查询到[{}]条数据,处理数据条数[{}]", totalHits.value, length);
if (searchResponse.status().getStatus() == 200) {
// 解析对象
return setSearchResponse(searchResponse, highlightField);
}
return null;
}
/**
* 高亮结果集 特殊处理
*
* @param searchResponse
* @param highlightField
*/
private static List<Map<String, Object>> setSearchResponse(SearchResponse searchResponse, String highlightField) {
List<Map<String, Object>> sourceList = new ArrayList<Map<String, Object>>();
StringBuffer stringBuffer = new StringBuffer();
for (SearchHit searchHit : searchResponse.getHits().getHits()) {
searchHit.getSourceAsMap().put("id", searchHit.getId());
if (StringUtils.isNotEmpty(highlightField)) {
System.out.println("遍历 高亮结果集,覆盖 正常结果集" + searchHit.getSourceAsMap());
Text[] text = searchHit.getHighlightFields().get(highlightField).getFragments();
if (text != null) {
for (Text str : text) {
stringBuffer.append(str.string());
}
//遍历 高亮结果集,覆盖 正常结果集
searchHit.getSourceAsMap().put(highlightField, stringBuffer.toString());
}
}
sourceList.add(searchHit.getSourceAsMap());
}
return sourceList;
}
}
3.5 启动类
package com.example.springbootelasticsearch3;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootElasticsearch3Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootElasticsearch3Application.class, args);
}
}
3.6 测试类
package com.example.springbootelasticsearch3;
import com.alibaba.fastjson.JSONObject;
import com.example.springbootelasticsearch3.utils.ElasticsearchUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.DateUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;
/**
* @author zhangshixing
* @date 2021年11月11日 12:43
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class GoodsResiportyTest {
/**
* 类型
*/
private String esType = "external";
/**
* 索引
*/
private String indexName = "test_index";
/**
* 创建索引
*/
@Test
public void createIndex() {
if (!ElasticsearchUtil.isIndexExist(indexName)) {
ElasticsearchUtil.createIndex(indexName);
} else {
System.out.print("索引已经存在");
}
System.out.print("索引创建成功");
}
/**
* 删除索引
*/
@Test
public void deleteIndex() {
if (!ElasticsearchUtil.isIndexExist(indexName)) {
System.out.print("索引不存在");
} else {
ElasticsearchUtil.deleteIndex(indexName);
}
System.out.print("索引删除成功");
}
/**
* 索引是否存在
*/
@Test
public void cexistsIndex() {
if (!ElasticsearchUtil.isIndexExist(indexName)) {
System.out.print("索引不存在");
} else {
System.out.print("索引存在");
}
}
/**
* 指定索引插入数据
*/
@Test
public void insertJson() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", DateUtils.formatDate(new Date()));
jsonObject.put("age", 25);
jsonObject.put("name", "j-" + new Random(100).nextInt());
jsonObject.put("createTime", new Date());
String id = ElasticsearchUtil.addData(jsonObject, indexName, esType, jsonObject.getString("id"));
}
@Test
public void delete() {
String id = "12";
if (StringUtils.isNotBlank(id)) {
ElasticsearchUtil.deleteDataById(indexName, esType, id);
System.out.print("删除id=" + id);
} else {
System.out.print("id为空");
}
}
@Test
public void queryMatchData() {
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolean matchPhrase = false;
if (matchPhrase == Boolean.TRUE) {
boolQuery.must(QueryBuilders.matchPhraseQuery("name", "j"));
} else {
boolQuery.must(QueryBuilders.matchQuery("name", "j"));
}
List<Map<String, Object>> list = ElasticsearchUtil.searchListData(indexName, esType, boolQuery, 10, null, null, null);
System.out.print(JSONObject.toJSONString(list));
}
}
{"size":10,"query":{"bool":{"must":[{"match":{"name":{"query":"j","operator":"OR","prefix_length":0,"max_expansions":50,"fuzzy_transpositions":true,"lenient":false,"zero_terms_query":"NONE","auto_generate_synonyms_phrase_query":true,"boost":1.0}}}],"adjust_pure_negative":true,"boost":1.0}},"_source":{"includes":[],"excludes":[]}}
2022-06-23 10:59:36.190 INFO 18272 --- [ main] c.e.s.utils.ElasticsearchUtil : 共查询到[1]条数据,处理数据条数[1]
[{"createTime":"2022-06-23T02:56:35.337Z","name":"j--1193959466","id":"Thu, 23 Jun 2022 02:56:35 GMT","age":25}]