本文ES版本为7.8.0 兼容很多新版本,代码都经过测试, 无坑, 大家放心食用
安装ES环境, 安装IK分词器,见博文: https://rourou.blog.csdn.net/article/details/101061408
由于es各个版本变化很大,所以在选择依赖的时候要尤为注意!!!我们可以通过spring的官方网站来进行查询版本对应的信息
https://docs.spring.io/spring-data/elasticsearch/docs/4.0.2.RELEASE/reference/html/#new-features
大家可以看到最新的官方文档更新到了7.6.2, 但是我们的版本是7.8.0,不过这个版本也是可以放心食用的.
我们一般使用spring的时候都用starter这边我们直接引入依赖
org.springframework.boot
spring-boot-starter-data-elasticsearch
2.4.0-SNAPSHOT
spring:
elasticsearch:
rest:
uris: 47.114.142.39:9200
read-timeout: 30s
connection-timeout: 5s
无需单独写配置文件: 因为starter会帮我们自动依赖
我们利用注解即可完成Index的创建, 和传统的json配置文件相比更加直观
package com.gupao.springbootdemo.bean;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.util.List;
/**
* 功能描述:ES的用户
*
* @Author: zhouzhou
* @Date: 2020/7/30$ 9:57$
*/
@Data
@Document(indexName = "es_user")
public class ESUser {
@Id
private Long id;
@Field(type = FieldType.Text)
private String name;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Keyword)
private List tags;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String desc;
}
注解的含义我简单介绍下:
@Document代表定义这个是文档, 其中indexName就是索引库的名字必须标注
@Id是标识文档的唯一id
@Field 就是字段了, 其中type为KeyWord代表这个字段不分词, analyzer是存数据的时候用的分词器
具体的含义和其他的注解请看官方文档
https://docs.spring.io/spring-data/elasticsearch/docs/4.0.2.RELEASE/reference/html/#elasticsearch.mapping
这是一个接口类, 类似我们的mybatis,其中有save方法(低版本应该是index方法),平时大多数常用的curd操作都可以用这个类来进行
支持编写派生方法!
package com.gupao.springbootdemo.repository;
import com.gupao.springbootdemo.bean.ESUser;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
/**
* 功能描述:
*
* @Author: zhouzhou
* @Date: 2020/7/30$ 14:17$
*/
public interface EsUserRepository extends ElasticsearchRepository {
long deleteESUserByName(String name);
List queryESUserByName(String name);
}
本demo包括:
package com.gupao.springbootdemo.controller;
import com.gupao.springbootdemo.bean.ESUser;
import com.gupao.springbootdemo.repository.EsUserRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 功能描述:
*
* @Author: zhouzhou
* @Date: 2020/7/30$ 14:35$
*/
@Api(tags = "es用户测试")
@RestController
@RequestMapping("/es/user")
public class EsUserController {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Autowired
private EsUserRepository esUserRepository;
@RequestMapping(value = "/create-index", method = RequestMethod.POST)
@ApiOperation("创建索引")
public Object createEsIndex() {
boolean index = elasticsearchRestTemplate.createIndex(ESUser.class);
elasticsearchRestTemplate.putMapping(ESUser.class);
System.out.println("创建索引结果是" + index);
return index;
}
@RequestMapping(value = "/delete-index", method = RequestMethod.POST)
@ApiOperation("删除索引")
public Object deleteEsIndex() {
boolean deleteIndex = elasticsearchRestTemplate.deleteIndex(ESUser.class);
System.out.println("删除索引结果是" + deleteIndex);
return deleteIndex;
}
@RequestMapping(value = "/exist-index", method = RequestMethod.POST)
@ApiOperation("是否存在索引")
public Object existEsIndex() {
boolean existsIndex = elasticsearchRestTemplate.indexExists(ESUser.class);
System.out.println("是否存在的结果是" + existsIndex);
return existsIndex;
}
@RequestMapping(value = "/save-doc", method = RequestMethod.POST)
@ApiOperation("保存文档")
public ESUser saveEsDoc(@RequestBody ESUser esUser) {
ESUser result = esUserRepository.index(esUser);
return result;
}
@RequestMapping(value = "/query-doc", method = RequestMethod.GET)
@ApiOperation("根据名字查询文档")
public List queryByName(String name) {
List result = esUserRepository.queryESUserByName(name);
return result;
}
@RequestMapping(value = "/exist-doc", method = RequestMethod.GET)
@ApiOperation("根据id查询文档")
public Object existDoc(Long id) {
return esUserRepository.existsById(id);
}
//---------------- 复杂查询 ------------------
@RequestMapping(value = "/query-doc/complex", method = RequestMethod.POST)
@ApiOperation("根据名字查询文档")
public Object queryByName(@RequestBody ESUser esUser) {
String desc = esUser.getDesc();
List tags = esUser.getTags();
String name = esUser.getName();
// 先构建查询条件
BoolQueryBuilder defaultQueryBuilder = QueryBuilders.boolQuery();
if (StringUtils.isNotBlank(desc)){
defaultQueryBuilder.should(QueryBuilders.termQuery("desc", desc));
}
if (StringUtils.isNotBlank(name)){
defaultQueryBuilder.should(QueryBuilders.termQuery("name", name));
}
if (!CollectionUtils.isEmpty(tags)){
for (String tag : tags) {
defaultQueryBuilder.must(QueryBuilders.termQuery("tags", tag));
}
}
// 分页条件
PageRequest pageRequest = PageRequest.of(0,10);
// 高亮条件
HighlightBuilder highlightBuilder = getHighlightBuilder("desc", "tags");
// 排序条件
FieldSortBuilder sortBuilder = SortBuilders.fieldSort("age").order(SortOrder.DESC);
//组装条件
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(defaultQueryBuilder)
.withHighlightBuilder(highlightBuilder)
.withPageable(pageRequest)
.withSort(sortBuilder).build();
SearchHits searchHits = elasticsearchRestTemplate.search(searchQuery, ESUser.class);
// 高亮字段映射
List userVoList = Lists.newArrayList();
for (SearchHit searchHit : searchHits) {
ESUser content = searchHit.getContent();
ESUserVo esUserVo = new ESUserVo();
BeanUtils.copyProperties(content,esUserVo);
Map> highlightFields = searchHit.getHighlightFields();
for (String highlightField : highlightFields.keySet()) {
if (StringUtils.equals(highlightField,"tags")){
esUserVo.setTags(highlightFields.get(highlightField));
}else if(StringUtils.equals(highlightField,"desc")){
esUserVo.setDesc(highlightFields.get(highlightField).get(0));
}
}
userVoList.add(esUserVo);
}
// 组装分页对象
Page userPage = new PageImpl<>(userVoList,pageRequest,searchHits.getTotalHits());
return userPage;
}
// 设置高亮字段
private HighlightBuilder getHighlightBuilder(String... fields) {
// 高亮条件
HighlightBuilder highlightBuilder = new HighlightBuilder(); //生成高亮查询器
for (String field : fields) {
highlightBuilder.field(field);//高亮查询字段
}
highlightBuilder.requireFieldMatch(false); //如果要多个字段高亮,这项要为false
highlightBuilder.preTags(""); //高亮设置
highlightBuilder.postTags("");
//下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等
highlightBuilder.fragmentSize(800000); //最大高亮分片数
highlightBuilder.numOfFragments(0); //从第一个分片获取高亮片段
return highlightBuilder;
}
}
7. 通过swagger文档进行复杂查询测试
搜索条件如下:
搜索结果如下: 分页信息和高亮都展示出来了
{
"content": [
{
"id": 7,
"name": "李8",
"age": 19,
"tags": [
"Java",
"Python"
],
"desc": "精通Java,Python的能力,爱好吃西瓜"
},
{
"id": 8,
"name": "李9",
"age": 19,
"tags": [
"Java",
"Python"
],
"desc": "精通Java,Python的能力,爱好吃西瓜"
},
{
"id": 1,
"name": "张三",
"age": 18,
"tags": [
"Java",
"Python"
],
"desc": "精通Java,Linux,Python的能力,爱好吃瓜"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 0,
"pageNumber": 0,
"pageSize": 10,
"paged": true,
"unpaged": false
},
"last": true,
"totalElements": 3,
"totalPages": 1,
"number": 0,
"size": 10,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"numberOfElements": 3,
"first": true,
"empty": false
}