Elasticsearch 7.7.1
1. elasticsearch-rest-high-level-client
添加依赖
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.7.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.7.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
添加配置
elasticsearch:
# 协议
schema: http
# 集群地址,多个用逗号隔开
nodes: 192.168.6.101:9200
# 连接超时时间
connect-timeout-ms: 5000
# Socket 连接超时时间
socket-timeout-ms: 5000
# 获取连接的超时时间
connection-request-timeout-ms: 5000
# 最大连接数
max-connect-total: 100
# 最大路由连接数
max-connect-per-route: 100
初始化
@Configuration
public class ElasticsearchConfig {
@Value("${elasticsearch.schema:http}")
private String schema;
@Value("${elasticsearch.nodes}")
private String nodes;
@Value("${elasticsearch.connect-timeout-ms:5000}")
private int connectTimeoutMs;
@Value("${elasticsearch.socket-timeout-ms:10000}")
private int socketTimeoutMs;
@Value("${elasticsearch.connection-request-timeout-ms:5000}")
private int connectionRequestTimeoutMs;
@Value("${elasticsearch.max-connect-total:100}")
private int maxConnectTotal;
@Value("${elasticsearch.max-connect-per-route:100}")
private int maxConnectPerRoute;
@Bean(destroyMethod = "close")
public RestHighLevelClient restHighLevelClient() {
// 拆分地址
List<HttpHost> hostLists = new ArrayList<>();
String[] hostList = nodes.split(",");
for (String addr : hostList) {
String host = addr.split(":")[0];
String port = addr.split(":")[1];
hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
}
HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});
// 构建连接对象
RestClientBuilder builder = RestClient.builder(httpHost);
// 异步连接延时配置
builder.setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(connectTimeoutMs);
requestConfigBuilder.setSocketTimeout(socketTimeoutMs);
requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeoutMs);
return requestConfigBuilder;
});
// 异步连接数配置
builder.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.setMaxConnTotal(maxConnectTotal);
httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
return httpClientBuilder;
});
return new RestHighLevelClient(builder);
}
}
定义ES文档对象
public class ElasticsearchDocument<T> {
private String id;
private T data;
// getter and setter
}
ES常用操作
@Service
public class ElasticsearchServiceImpl implements ElasticsearchService {
private static final Logger logger = LoggerFactory.getLogger(ElasticsearchServiceImpl.class);
private static final int DEFAULT_SHARDS = 1;
private static final int DEFAULT_REPLICAS = 1;
@Autowired
private RestHighLevelClient restHighLevelClient;
@Override
public void createIndex(String indexName, Map<String, Map<String, Object>> properties) {
try {
XContentBuilder mapping = XContentFactory.jsonBuilder();
mapping.startObject()
.startObject("mappings")
.field("properties", properties)
.endObject()
.startObject("settings")
.field("number_of_shards", DEFAULT_SHARDS)
.field("number_of_replicas", DEFAULT_REPLICAS)
.endObject()
.endObject();
CreateIndexRequest request = new CreateIndexRequest(indexName).source(mapping);
CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
boolean isCreated = response.isAcknowledged();
logger.info("索引 {} 创建{}", indexName, isCreated ? "成功" : "失败");
} catch (IOException e) {
logger.error(e.getMessage());
}
}
@Override
public void deleteIndex(String indexName) {
try {
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
boolean isDeleted = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged();
logger.info("索引 {} 删除{}", indexName, isDeleted ? "成功" : "失败");
} catch (IOException e) {
logger.error(e.getMessage());
}
}
@Override
public void save(String indexName, ElasticsearchDocument<?> document) {
IndexRequest request = new IndexRequest(indexName)
.id(document.getId())
.source(JSON.toJSONString(document.getData()), XContentType.JSON);
try {
restHighLevelClient.index(request, RequestOptions.DEFAULT);
} catch (IOException e) {
logger.error(e.getMessage());
}
}
@Override
public <T> void save(String indexName, List<ElasticsearchDocument<T>> documentList) {
// 批量请求
BulkRequest bulkRequest = new BulkRequest();
documentList.forEach(doc -> {
bulkRequest.add(new IndexRequest(indexName)
.id(doc.getId())
.source(JSON.toJSONString(doc.getData()), XContentType.JSON));
});
try {
restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
logger.error(e.getMessage());
}
}
@Override
public void deleteDocument(String indexName, String id) {
DeleteRequest deleteRequest = new DeleteRequest(indexName, id);
try {
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
logger.error(e.getMessage());
}
}
}
测试
public class UserDetail {
private String username;
private String nickname;
private Integer age;
private String address;
private Long createTime;
// getter and setter
}
@SpringBootTest
public class ElasticsearchServiceTest {
@Autowired
private ElasticsearchService elasticsearchService;
private final String INDEX_NAME = "user_detail";
@Test
public void createIndex() throws IOException {
Map<String, Object> chineseMap = new HashMap<>(3);
chineseMap.put("type", "text");
chineseMap.put("analyzer", "ik_max_word");
chineseMap.put("search_analyzer", "ik_max_word");
Map<String, Object> integerMap = new HashMap<>(1);
integerMap.put("type", "integer");
Map<String, Object> longMap = new HashMap<>(1);
longMap.put("type", "long");
Map<String, Map<String, Object>> properties = new HashMap<>(5);
properties.put("username", chineseMap);
properties.put("nickname", chineseMap);
properties.put("age", integerMap);
properties.put("address", chineseMap);
properties.put("createTime", longMap);
elasticsearchService.createIndex(INDEX_NAME, properties);
}
@Test
public void deleteIndex() {
elasticsearchService.deleteIndex(INDEX_NAME);
}
@Test
public void save() {
UserDetail userDetail = new UserDetail("张一", "章亿", 18, "北京", new Date().getTime());
elasticsearchService.save(INDEX_NAME, new ElasticsearchDocument<>(UUID.randomUUID().toString(), userDetail));
}
@Test
public void saveAll() {
List<ElasticsearchDocument<UserDetail>> documentList = new ArrayList<>(5);
UserDetail userDetail1 = new UserDetail("王二", "忘耳", 20, "上海", new Date().getTime());
UserDetail userDetail2 = new UserDetail("张三", "丈叁", 22, "广州", new Date().getTime());
UserDetail userDetail3 = new UserDetail("李四", "丽丝", 24, "深圳", new Date().getTime());
documentList.add(new ElasticsearchDocument<>(UUID.randomUUID().toString(), userDetail1));
documentList.add(new ElasticsearchDocument<>(UUID.randomUUID().toString(), userDetail2));
documentList.add(new ElasticsearchDocument<>(UUID.randomUUID().toString(), userDetail3));
elasticsearchService.save(INDEX_NAME, documentList);
}
@Test
public void delete() {
elasticsearchService.deleteDocument(INDEX_NAME, "743a6866-c491-4502-b1ec-ad3da3ea2132");
}
}
测试查询和分页
@SpringBootTest
public class UserDetailTest {
@Autowired
private RestHighLevelClient restHighLevelClient;
private final String INDEX_NAME = "user_detail";
/**
* 范围查询
*/
@Test
public void range() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.rangeQuery("age").gte(20).lte(25));
this.getUserDetailResult(searchSourceBuilder);
}
/**
* 布尔查询
*/
@Test
public void bool() {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("username", "三")).filter(QueryBuilders.rangeQuery("age").gte(20).lte(25));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(boolQueryBuilder);
this.getUserDetailResult(searchSourceBuilder);
}
/**
* 简单查询字符串
*/
@Test
public void simpleQueryString() {
Map<String, Float> filedMap = new HashMap<>(3);
filedMap.put("username", 0.3f);
filedMap.put("nickname", 0.2f);
filedMap.put("address", 0.1f);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.filter(QueryBuilders.simpleQueryStringQuery("张").fields(filedMap).defaultOperator(Operator.OR));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(boolQueryBuilder);
this.getUserDetailResult(searchSourceBuilder);
}
private void getUserDetailResult(SearchSourceBuilder searchSourceBuilder) {
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
if (RestStatus.OK.equals(searchResponse.status())) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
UserDetail userInfo = JSON.parseObject(hit.getSourceAsString(), UserDetail.class);
System.out.println(userInfo.toString());
}
}
}
/**
* 年龄平均值
*/
@Test
public void avg() {
try {
AggregationBuilder aggregationBuilder = AggregationBuilders.avg("age_avg").field("age");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(aggregationBuilder);
SearchRequest request = new SearchRequest(INDEX_NAME);
request.source(searchSourceBuilder);
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
Aggregations aggregations = response.getAggregations();
if (RestStatus.OK.equals(response.status()) || aggregations != null) {
ParsedAvg parsedAvg = aggregations.get("age_avg");
System.out.println(parsedAvg.getValue());
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* from size 分页
*/
@Test
public void pageByFromSize() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery()).from(0).size(2);
this.getUserDetailResult(searchSourceBuilder);
}
/**
* scroll 分页
*/
@Test
public void pageByScroll() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery()).size(2);
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
searchRequest.source(searchSourceBuilder).scroll(TimeValue.timeValueMinutes(1L));
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("scroll_id:" + searchResponse.getScrollId());
} catch (IOException e) {
e.printStackTrace();
}
if (RestStatus.OK.equals(searchResponse.status())) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
UserDetail userInfo = JSON.parseObject(hit.getSourceAsString(), UserDetail.class);
System.out.println(userInfo.toString());
}
}
}
/**
* 根据scroll_id查询
*/
@Test
public void pageByScrollId() {
String scrollId = "...";
SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
System.out.println("scroll_id" + searchResponse.getScrollId());
} catch (IOException e) {
e.printStackTrace();
}
if (RestStatus.OK.equals(searchResponse.status())) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
UserDetail userInfo = JSON.parseObject(hit.getSourceAsString(), UserDetail.class);
System.out.println(userInfo.toString());
}
}
}
/**
* 清除scroll_id
*/
@Test
public void clearScrollId() {
String scrollId = "...";
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.addScrollId(scrollId);
ClearScrollResponse clearScrollResponse = null;
try {
clearScrollResponse = restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
System.out.println(clearScrollResponse.isSucceeded());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* search_after 分页
*/
@Test
public void pageBySearchAfter() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery()).size(2).sort("_id", SortOrder.ASC);
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
if (RestStatus.OK.equals(searchResponse.status())) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
UserDetail userInfo = JSON.parseObject(hit.getSourceAsString(), UserDetail.class);
System.out.println(userInfo.toString());
System.out.println("sort:" + hit.getSortValues()[0]);
}
}
}
/**
* 根据最后一条sort值查询
*/
@Test
public void pageBySearchAfter2() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery()).size(2).sort("_id", SortOrder.ASC).searchAfter(new Object[]{"..."});
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
if (RestStatus.OK.equals(searchResponse.status())) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
UserDetail userInfo = JSON.parseObject(hit.getSourceAsString(), UserDetail.class);
System.out.println(userInfo.toString());
System.out.println("sort:" + hit.getSortValues()[0]);
}
}
}
}
2. Spring Data Elasticsearch
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
</dependencies>
添加配置
spring:
elasticsearch:
rest:
uris: http://localhost:9200
定义User类,映射ES的索引
@Document(indexName = "user")
public class User {
@Id
private String id;
@Field(type = FieldType.Text, searchAnalyzer = "ik_max_word", analyzer = "ik_max_word")
private String username;
@Field(type = FieldType.Text, searchAnalyzer = "ik_max_word", analyzer = "ik_max_word")
private String nickname;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Text, searchAnalyzer = "ik_max_word", analyzer = "ik_max_word")
private String address;
@Field(type = FieldType.Long)
private Date createTime;
// getter and setter
}
@Document
:类级别,映射到索引,默认会自动创建索引。
@Id
:字段级别,标识id。
@Field
:字段级别,定义字段的属性:
- name:字段名称,如果未设置,则使用字段名称。
- type:字段类型
- analyzer,searchAnalyzer,normalizer:指定自定义analyzers 和 normalizer。
定义UserRepository
public interface UserRepository extends ElasticsearchRepository<User, String> {
List<User> findByUsername(String username);
List<User> findByAgeBetween(Integer startAge, Integer endAge);
Page<User> findAll(Pageable pageable);
}
测试
@SpringBootTest
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
private final String INDEX_NAME = "user";
@Test
public void insert() {
User user = new User("张五", "张五昵称", 30, "五五路5号", new Date());
userRepository.save(user);
}
@Test
public void search() {
List<User> userList = userRepository.findByUsername("三");
System.out.println(userList);
}
@Test
public void update() {
List<User> userList = userRepository.findByUsername("张一");
userList.forEach(user -> {
user.setUsername(user.getUsername() + "更新");
});
userRepository.saveAll(userList);
}
@Test
public void delete() {
List<User> userList = userRepository.findByUsername("张一");
userRepository.deleteAll(userList);
}
/**
* 范围查询
*/
@Test
public void findByAgeBetween() {
List<User> userList = userRepository.findByAgeBetween(20, 25);
System.out.println(userList);
}
/**
* from size 分页
*/
@Test
public void pageByFormSize() {
Pageable pageable = PageRequest.of(0, 2);
Page<User> userPage = userRepository.findAll(pageable);
System.out.println(userPage.getContent());
}
/**
* scroll 分页
*/
@Test
public void pageByScroll() {
IndexCoordinates index = IndexCoordinates.of(INDEX_NAME);
Query query = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withPageable(PageRequest.of(0, 2))
.build();
SearchScrollHits<User> searchScrollHits = elasticsearchRestTemplate.searchScrollStart(6000, query, User.class, index);
System.out.println("scroll_id:" + searchScrollHits.getScrollId());
searchScrollHits.get().forEach(searchHit -> {
System.out.println(searchHit.getContent());
});
}
/**
* 根据 scroll_id 查询
*/
@Test
public void pageByScrollId() {
IndexCoordinates index = IndexCoordinates.of(INDEX_NAME);
String scrollId = "...";
SearchScrollHits<User> searchScrollHits = elasticsearchRestTemplate.searchScrollContinue(scrollId, 6000, User.class, index);
System.out.println("scroll_id:" + searchScrollHits.getScrollId());
searchScrollHits.get().forEach(searchHit -> {
System.out.println(searchHit.getContent());
});
}
}
参考:
ES 官方文档
Spring Boot 集成 ElasticSearch (7.x 版本)搜索引擎
SpringBoot 操作 ElasticSearch 详解(万字长文)
Spring Data Elasticsearch 官方文档
Spring Boot 集成 Elasticsearch 实战