1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
导入依赖后,注意在依赖中查看对应的版本是否与本机ES对应
2.创建配置并编写测试类
@Configuration
public class ElasticSearchConfig {
// 注册 rest高级客户端
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1",9200,"http")
)
);
return client;
}
}
测试类测试各种常用API:
@SpringBootTest(classes = ElasticSearchApp.class)
@RunWith(SpringRunner.class)
public class ElasticSearchTest {
@Autowired
private RestHighLevelClient restHighLevelClient;
// 测试索引的创建, Request PUT liuyou_index
@Test
public void createIndex() throws IOException {
//创建索引请求
CreateIndexRequest request=new CreateIndexRequest("create");
//客户端执行请求 请求后获得响应
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
//获取索引
GetIndexRequest getIndexRequest=new GetIndexRequest("create");
//查看索引是否存在
boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
System.out.println(exists);
//删除索引
DeleteIndexRequest deleteIndexRequest=new DeleteIndexRequest();
AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
//删除索引
@Test
public void DeleteIndex() throws IOException {
DeleteIndexRequest deleteIndexRequest=new DeleteIndexRequest("create");
AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
//添加文档
@Test
public void addDocument() throws IOException {
//创建对象
Manager manager=new Manager(1,"王五","123");
IndexRequest indexRequest=new IndexRequest("test2");
//规则 PUT /test2/_doc/1
indexRequest.id("1");
indexRequest.timeout(TimeValue.timeValueSeconds(1));
//将数据类型放入请求 JSON格式
indexRequest.source(JSON.toJSONString(manager), XContentType.JSON);
//客户端发送请求 获取响应结果
IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(indexResponse.toString());
System.out.println(indexResponse.status()); //对应我们命令返回的状态结果
}
//判断文档是否存在
@Test
public void testGetDocumentExists() throws IOException {
//获取文档 判断是否存在
GetRequest getRequest=new GetRequest("test2","1");
//不获取_source的上下文了
getRequest.fetchSourceContext(new FetchSourceContext(false));
getRequest.storedFields("_none_");
boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
/*-----------------------------------*/
//获取文档信息
}
//获取文档的信息
@Test
public void testGetDocument() throws IOException {
//获取文档 判断是否存在
GetRequest getRequest=new GetRequest("test2","1");
//不获取_source的上下文了
GetResponse documentFields = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
System.out.println(documentFields.getSourceAsString());//打印文档内容
System.out.println(documentFields); //返回的是全部内容 和命令一样
}
//更新文档
@Test
public void updateDocument() throws IOException {
//获取文档 判断是否存在
UpdateRequest updateRequest=new UpdateRequest("test2","1");
Manager manager=new Manager(1,"老六","666");
//将JSON格式的对象添加
updateRequest.doc(JSON.toJSONString(manager),XContentType.JSON);
UpdateResponse update = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(update.status());
restHighLevelClient.close();
}
//删除文档
@Test
public void DeleteDocument() throws IOException {
DeleteRequest deleteRequest=new DeleteRequest("test2","1");
deleteRequest.timeout("1s");
DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(delete.status());
restHighLevelClient.close();
}
//批量插入数据 真实环境一般都是批量插入
@Test
public void BulkDocument() throws IOException {
BulkRequest bulkRequest=new BulkRequest();
bulkRequest.timeout("10s"); //数据越大最好设置越大
ArrayList<Manager> list=new ArrayList<>();
list.add(new Manager(2,"哈哈结","886"));
list.add(new Manager(2,"特朗噗","856"));
list.add(new Manager(2,"王某某","999"));
//批处理请求
for (int i = 0; i < list.size(); i++) {
bulkRequest.add(
new IndexRequest("test2")
.id(""+(i+1))
.source(JSON.toJSONString(list.get(i)),XContentType.JSON));
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.hasFailures()); //是否失败 返回false 代表成功
}
// 查询
// SearchRequest 搜索请求
// SearchSourceBuilder 条件构造
// HighLightBuilder 构建高亮
// TermQueryBuilder 精确查询
//
//搜索
@Test
public void testSearch() throws IOException {
SearchRequest searchRequest=new SearchRequest("test2");
//构建搜索条件
SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
searchSourceBuilder.highlighter(); //高亮
//查询条件,可以使用QueryBuilders工具来实现
//QueryBuilders.termQuery 精确
//QueryBuilders.matchAllQuery() 匹配所有
TermQueryBuilder termQueryBuilder= QueryBuilders.termQuery("username","肖");
searchSourceBuilder.query(termQueryBuilder);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(searchSourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
}
}
API总结:其使用规则一般先创建对应的请求Request,例如updateRequest、GetRequest.... 然后请求对象中输入对应的索引值。然后操作各个对象的属性,最后使用client对象进行操作。
3.具体业务实现(个人项目演示)
💡思路:
1.首先需要将数据库中的所有数据,同步到ElasticSearch中。这边先编写好对应的添加类。
2.数据同步完成后,编写相应的搜索业务类。
3.还应该封装一个ES各种操作类,如增删查改等(也可以用现成的ESTemplate)。
💡注意事项:
将文档的id值和数据的id值设为一个值。
在编写ES的操作类时想到一个问题,一个文档数据在es中有一个id值,相当于数据库中每一行的一个主键id值。而操作文档数据需要用到这个id值(发生修改时等),如若文档id值和数据本身的一个id值不一致的话,那么会非常不便。所以需要在批量插入数据的时候,需要将文档id和数据id一一对应。
//批量插入升级版,文档id值和对象id值一一对应(便于后续删改操作)
//其中的id值--指的是在es索引中文档的id值 相当于数据库中每一行数据的id值 需要将这两个id值设为同一个
public void bulkByIdDocument(Map<String,Object> map, String index){
ConnectElasticSearch.connect(client -> {
BulkRequest bulkRequest=new BulkRequest(index);
bulkRequest.timeout(EsConfig.DSTimeOut);
for (String id : map.keySet()) {
Object o = map.get(id);
bulkRequest.add(
new IndexRequest(index)
.id(id)
.source(JSON.toJSONString(map.get(id)),XContentType.JSON)
);
}
BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println("添加是否失败=" + bulkResponse.hasFailures()); //返回false代表成功
});
}
将传入参数改为Map<String,Object>结构,其中String对象的id值。Object为该对象。在外部将List中的对象循环取出依次存入这个map即可。
public void addPhone(){
List<EsPhone> list = esPhoneDao.findAll();
Map<String,Object> map=new HashMap<>();
for (EsPhone esPhone : list) {
map.put(String.valueOf(esPhone.getId()),esPhone);
}
esService.bulkByIdDocument(map,EsConfig.index);
}
结果显示如图:
💡关于分词器与搜索匹配的坑:
在使用term匹配时候,发现输入两个字以上的中文时搜索不出任何东西出来。后续是排查出索引字段是用的默认standard分词器,而默认中文分词器会把中文全部拆开,所以两字以上无法匹配出来。后续又尝试修改索引分词器,但没有效果。最终删除索引,手动创建索引并给相应的字段添加ik分词器后得到解决。
自定义索引创建规则
PUT /xiaomi-phone
{
"mappings": {
"properties": {
"cap":{
"type": "text"
},
"color":{
"type": "text"
},
"price":{
"type": "float"
},
"num":{
"type": "long"
},
"name":{
"type": "text",
"analyzer": "ik_max_word"
},
"pid":{
"type": "long"
},
"id":{
"type": "long"
},
"brand":{
"type": "text"
, "analyzer": "ik_smart"
},
"pics":{
"type": "text"
}
}
}
}