Springboot整合Elasticsearch实现实时搜索
前端页面采用的是github上看到的页面使用Vue实现,GitHub原址:https://github.com/lavyun/vue-demo-search ,后台页面采用springboot+es实现。
Springboot项目构建省略,不会的朋友参考。(springboot入门)
Elasticsearch安装步骤省略,没安装的参考。(Elasticsearch环境搭建和介绍(Windows))
Vue项目构建略过。( Vue-cli 快速构建Vue项目)
创建SpringBoot 项目
首先新建一个SpringBoot项目,再进行Elasticsearch的整合实现搜索。
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>aliyunmaven</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
application.yml
server:
port: 8888
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.43.238:9300 #ip地址填写自己的
jackson:
default-property-inclusion: non_null #如果是空值则不返回
索引与查询实例
@Data
@Document(indexName = "goodscat", type = "docs", shards = 1, replicas = 0)
public class Goods {
@Id
private Long cid;
// 所有需要被搜索的信息,包含标题,分类,甚至品牌
@Field(type = FieldType.Keyword, index = true, analyzer = "ik_max_word")
private String name; //分类名称,
private String isParent;
private String parentId;
private Long level;
private String pathid;
}
Spring Data通过注解来声明字段的映射属性,上面三个注解代表的意思为:
- @Document 作用在类,标记实体类为文档对象,一般有两个属性
- indexName:对应索引库名称
- type:对应在索引库中的类型
- shards:分片数量,默认5
- @Id 作用在成员变量,标记一个字段作为id主键
- @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
- type:字段类型,是枚举:FieldType,可以是text、long、short、date、integer、object等
- text:存储数据时候,会自动分词,并生成索引
- keyword:存储数据时候,不会分词建立索引
- Numerical:数值类型,分两类
- 基本数据类型:long、interger、short、byte、double、float、half_float
- 浮点数的高精度类型:scaled_float
- 需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。
- Date:日期类型
- elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。
- index:是否索引,布尔类型,默认是true
- store:是否存储,布尔类型,默认是false
- analyzer:分词器名称,这里的ik_max_word即使用ik分词器
4. 创建搜索配置类
public class SearchRequest {
private String key;// 搜索条件
private Integer page;// 当前页
private static final Integer DEFAULT_SIZE = 10;// 每页大小,不从页面接收,而是固定大小
private static final Integer DEFAULT_PAGE = 1;// 默认页
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Integer getPage() {
if(page == null){
return DEFAULT_PAGE;
}
// 获取页码时做一些校验,不能小于1
return Math.max(DEFAULT_PAGE, page);
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getSize() {
return DEFAULT_SIZE;
}
}
- ElasticsearchRepository创建
dao层只需继承ElasticsearchRepository类。其中主要的方法就是 save、delete和search,其中save方法相当如insert和update,没有就新增,有就覆盖。delete方法主要就是删除数据以及索引库。至于search就是查询了。
package com.demo.elasticsearch.repository;
import com.demo.elasticsearch.pojo.Goods;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface GoodsRepository extends ElasticsearchRepository<Goods,Long> {
}
- service实现层(代码只有搜索的实现,数据在测试类中已经添加完成了,这里只做查询)
@Slf4j
@Service
public class SearchServiceImpl implements SearchService {
@Autowired
private GoodsRepository goodsRepository;
@Autowired
private ElasticsearchTemplate template;
@Override
public PageResult<Goods> search(SearchRequest request) {
//分页
int page = request.getPage() - 1;
int size = request.getSize();
// 定义高亮字段
HighlightBuilder.Field titleField = new HighlightBuilder.Field("name").preTags("<span style='color:red'>").postTags("</span>");
//创建查询构建器
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
//结果过滤,只查询cid,name字段
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"cid", "name"}, null));
//分页
queryBuilder.withPageable(PageRequest.of(page, size));
//过滤
queryBuilder.withQuery(QueryBuilders.matchQuery("name", request.getKey())).withHighlightFields(titleField);
//查询,在查询的基础上给字段设置高亮红色
AggregatedPage<Goods> result = template.queryForPage(queryBuilder.build(), Goods.class,
new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
List<Goods> list = new ArrayList<Goods>();
for (SearchHit searchHit : response.getHits()) {
if (response.getHits().getHits().length <= 0) {
return null;
}
Goods idea = new Goods();
HighlightField ideaTitle = searchHit.getHighlightFields().get("name");
if (ideaTitle != null) {
idea.setName(ideaTitle.fragments()[0].toString());
}
list.add(idea);
}
if (list.size() > 0) {
return new AggregatedPageImpl<T>((List<T>) list);
}
return null;
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
return null;
}
}
);
//解析结果
//分页结果解析
long total = result.getTotalElements();
Long totalPages = result.getTotalPages();
List<Goods> goodsList = result.getContent();
//封装分页数据
return new PageResult(total, totalPages, goodsList);
}
}
- Controller层(比较简单,单纯调用一下)
@RestController
public class SearchController {
@Autowired
private SearchService searchService;
/**
* 搜索功能
* @param request
* @return
*/
@PostMapping("search")
public ResponseEntity<PageResult<Goods>> search(@RequestBody SearchRequest request) {
return ResponseEntity.ok(searchService.search(request));
}
}
好了,代码编写完成,我们启动我们的项目测试一下。
总结
只是简单的实现了一个springboot整合Elasticsearch的案例,我们还可以利用spring data提供的Repository接口做很多事情,比如对同一分类进行聚合为桶等,天天学习,向大佬们靠近。