配置ElasticSearch
配置ElasticSearch的目的是为了让Springboot能够访问到ES,从而能够利用springboot发送Http请求,对ES进行相关的增删改查操作。常用的配置操作有RestHighLevelClient和RestTemplate,对于个人主机上的ES,一般使用RestHighLevelClient即可,但是对于使用过代理的服务器(没有端口号),要使用RestTemplate来发送Http请求。
RestHighLevelClient
构建项目时,要选择elasticsearch,这里的构建是从alibaba下载,从spring官方下载界面略有差异,但同样存在非关系型数据库elasticsearch选项
- 导入依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.6.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.2</version>
<scope>compile</scope>
</dependency>
这两个依赖为常用依赖,一般而言导入即可正常使用,但是当代码部署到服务器上时,可能会出现java.lang.NoClassDefFoundError错误,这个是因为缺少了ES的核心依赖,导入即可
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.6.2</version>
</dependency>
- 编写springboot配置文件
在resources文件夹下的application.properties文件(yml文件稍稍格式不同)中,编写elasticsearch.url,这样写的目的是防止代码写死,便于以后修改。在RestHighLevelClient可以选择不用,因为配置也是一次即可
spring.application.name=es_control #name一般自动生成
elasticsearch.url=http://127.0.0.1:9200/
- 编写ES配置类
package com.gz.es_control.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;
@Configuration
public class ElasticSearchClientConfig {
private String serverAddress;
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client=new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1",9200,"http")
)
);
return client;
}
}
这样,在@Bean注解下,返回的client将会自动生成Bean,注入到spring容器中(名字为restHighLevelClient)。
当然,可以选择多写一些其他信息,比如请求获取数据的超时时间等,个人学习不需要配置过多,详细配置也可查阅资料。
4. restHighLevelClient使用
编写controller层接口(仅写部分接口,其他接口大同小异)
添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
在controlller层代码中,import com.alibaba.fastjson.JSONObject;
注意不要选到其他的JSON类,可能导致无法接收到请求体的JSON数据
package com.example.demo.controller;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
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 com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
@RestController
@RequestMapping(value = "/")
public class ESController {
@Autowired
RestHighLevelClient restHighLevelClient;
//查询索引是否存在
@GetMapping(value = "/query/{indexName}")
public String existIndex(@PathVariable(name = "indexName")String indexName) throws IOException {
GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
if (!exists){
return "Index does not exist";
}else {
return "Index exist";
}
}
//创建索引
@PostMapping(value = "/create/{indexName}")
@ResponseBody
public CreateIndexResponse createIndex(@PathVariable(name = "indexName")String indexName,@RequestBody JSONObject jsonParam ) throws IOException {
CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
createIndexRequest.mapping(jsonParam.getJSONObject("mappings"));
System.out.println(jsonParam);//同样的方法,可以设置setting值,来决定集群分片数等
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
return createIndexResponse;
}
//删除索引
@DeleteMapping(value = "/delete/{indexName}")
public boolean deleteIndex(@PathVariable(name = "indexName")String indexName) throws IOException {
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest,RequestOptions.DEFAULT);
boolean acknowledged = delete.isAcknowledged();
return acknowledged;
}
}
查询和删除比较直接,来测试一下创建索引,看看ES中的mapping结构是否和传的值一样
使用postman发送请求,可以得到回应
使用elasticsearch-head-master查看
可以看到,ES中的索引mappings值确实和请求体一致
RestTemplate
前面提到,RestHighLevelClient和RestTemplate都能对ES进行操作,但是如果我们要对服务器的ES,而服务器又进行了代理,没有端口号,如http://111.111.111.111/es/为服务器es地址,那么这个时候RestHighLevelClient可能没有那么好操作。
RestTemlate可以解决和简化这个问题
创建Service层代码
package com.example.demo.service;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
@Service
public class IndexService {
@Value("${elasticsearch.rul}")
private String serverAddress;//这里用到了开头说的配置
//创建索引
public String createIndex(JSONObject jsonParam, String metadataName) throws IOException {
RestTemplate restTemplate=new RestTemplate();
boolean exists;
try {
restTemplate.getForObject(serverAddress+metadataName,Object.class);
exists=true;
}catch (Exception e){ exists=false; }
if(exists){
return "索引已存在,创建失败";
}
String url=serverAddress+metadataName;
restTemplate.put(url,jsonParam);
return "索引创建成功";
}
//删除索引
public String deleteIndex(String metadataName) throws IOException {
RestTemplate restTemplate=new RestTemplate();
boolean exists;
try {
restTemplate.getForObject(serverAddress+metadataName,Object.class);
exists=true;
}catch (Exception e){ exists=false; }
if(exists){
String url=serverAddress+metadataName;
restTemplate.delete(url);
return "删除索引成功";
}else{
return "索引不存在,删除失败";
}
}
//重索引
private void reindex(String oldIndex,String newIndex){
String url=serverAddress+"_reindex";
String str="{\n" +
" \"source\": {\n" +
" \"index\": \""+oldIndex+"\"\n" +
" },\n" +
" \"dest\": {\n" +
" \"index\": \""+newIndex+"\"\n" +
" }\n" +
"}";
JSONObject jsonObject=JSONObject.parseObject(str);
RestTemplate restTemplate=new RestTemplate();
restTemplate.postForObject(url,jsonObject,JSONObject.class);
}
//起别名
private void realiases(String index,String oldIndex,String newIndex){
String url=serverAddress+"_aliases";
String str="{\n" +
" \"actions\": [{\"add\": {\n" +
" \"index\": \""+newIndex+"\",\n" +
" \"alias\": \""+index+"\"\n" +
" }}, {\"remove\": {\n" +
" \"index\": \""+oldIndex+"\",\n" +
" \"alias\": \""+index+"\"\n" +
" }}]\n" +
"}";
JSONObject jsonObject=JSONObject.parseObject(str);
RestTemplate restTemplate=new RestTemplate();
restTemplate.postForObject(url,jsonObject,JSONObject.class);
}
//修改索引结构
public void modifyIndex(JSONObject jsonObject_mappings,String metadataName) throws IOException {
RestTemplate restTemplate=new RestTemplate();
boolean exists;
try {
restTemplate.getForObject(serverAddress+metadataName,Object.class);
exists=true;
}catch (Exception e){ exists=false; }
if (exists) {
createIndex(jsonObject_mappings, metadataName + "_es");
reindex(metadataName, metadataName + "_es");
deleteIndex(metadataName);
Thread.sleep(1000);//线程休眠一秒,防止数据丢失
createIndex(jsonObject_mappings, metadataName);
reindex(metadataName + "_es", metadataName);
deleteIndex(metadataName + "_es");
}else{
createIndex(jsonObject_mappings,metadataName);
}
}
}
elasticsearch两次reindex的时间间隔要大于一秒,否则可能数据丢失,所以这里Thread.sleep(1000);
Service层代码的目的是为了创建一个Bean,供Controller层代码使用。
编写Controller层代码
因为实现功能的代码都在IndexService中,所以这里Controller层代码比较简单,如果要实现其他功能,可以创建相应的Service的Bean,在Controller层调用
package com.example.demo.controller;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.example.demo.service.*;
import java.io.IOException;
@RestController
@RequestMapping(value = "/")
public class ESControllerRestTemplate {
@Autowired
private IndexService indexService;
@PostMapping(value = "/createIndexRestTemplate/{indexName}")
@ResponseBody
public String createIndex(@PathVariable(name = "indexName")String indexName , @RequestBody JSONObject jsonParam) throws IOException {
indexService.createIndex(jsonParam,indexName);
return "success";
}
@DeleteMapping(value = "/deleteIndexRestTemplate/{indexName}")
@ResponseBody
public String deleteIndex(@PathVariable(name = "indexName")String indexName) throws IOException {
indexService.deleteIndex(indexName);
return "success";
}
@PutMapping(value = "/modifyIndexRestTemplate/{indexName}")
@ResponseBody
public String modifyIndex(@PathVariable(name = "indexName")String indexName,@RequestBody JSONObject jsonParam) throws IOException {
indexService.modifyIndex(jsonParam,indexName);
return "success";
}
}
创建和删除等的功能跟RestHighLevelClient相同,可以实现创建索引、删除索引等。
这里测试修改索引的功能,ES建立索引后,mappings结构不能修改,但是ES提供了另外一个功能reindex,类似于删除并且重新创建索引(用新的mappings),同时将原来索引的数据迁移到新的索引中,保证数据不会丢失。
先看看索引中的结构和数据
然后调用/modifyIndexRestTemplate/tryagain接口
可以看到修改索引结构成功,再看看ES现在的结构
可以看到索引的结构已经改变,再看看数据
可以看到数据也没有丢失(因为所有数据都没有包含新字段的信息,所以新字段不显示,但是可以在mappings中看到结构已经修改,包含了新加的字段)。
同样可以利用RestTemplate实现增删改查、起别名等相关的ES操作,操作方法大同小异。
总结
RestHighLevelClient和RestTemplate都能对ES进行操作,RestTemplate的好处是可以解决当服务器的ES进行代理之后没有端口的情况,并且RestTemplate对ES的操作方法基本一致,没有过多的变化,可以结合前端传来的JSON数据,灵活性高,但用RestTemplate发送Http请求时要处理好JSON数据,保证请求体的正确性。
如果要简化前端的操作,不想前端书写完整的JSON数据,可以在springboot代码中编写部分JSON形式的String,前端传入对应的数据后,拼接成完整的JSON形式的String变量,然后利用
JSONObject jsonObject=JSONObject.parseObject(str);
将其转换为JSONObject,用于RestTemplate的请求体中。