系列课程 ElasticSearch 之第 8 篇 —— SpringBoot 整合 ElasticSearch 做查询(分页查询)-爱代码爱编程
本篇博客基于前面第4篇的基础上改造的:https://blog.csdn.net/BiandanLoveyou/article/details/115773729
可以下载前面的代码:https://pan.baidu.com/s/1GnLrGGrP7nhw2Xovq3MtxQ 提取码:mhbi
这里我们需要修改一下 pom.xml 文件,引入 Google 的工具包:
完整配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.study</groupId>
<artifactId>ElasticSearchTest</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
</parent>
<dependencies>
<!--Spring boot 集成包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- 引入 Google 的集合工具包 -->
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
为了测试方便,我们提前准备5条数据,使用 Kibana 管理页面进行创建。
首先创建文档并指定类型:创建文档名称为【blog】,类型为【info】。指定博客名称为【blogName】,并在索引创建的时候使用的分词器,搜索字段的值时也指定的分词器为中文分词器。博客描述【blogDesc】,博客点击数【clickCount】,博客地址【blogUrl】。注意ES的字符串类型可以指定为 text 和 Keyword。
POST /blog/info
{
"info":{
"properties":{
"blogName":{
"type":"text",
"analyzer":"ik_smart",
"search_analyzer":"ik_smart"
},
"blogDesc":{
"type":"text",
"analyzer":"ik_smart",
"search_analyzer":"ik_smart"
},
"clickCount":{
"type":"integer"
},
"blogUrl":{
"type":"keyword"
}
}
}
}
执行结果:
然后使用 Kibana 创建5条测试数据(我们先记录每条记录生成的ID):
POST /blog/info
{
"blogName":"了解ElasticSearch",
"blogDesc":"这个系列教程我们主要学习ElasticSearch。去到大型互联网公司,这可是必备技能!",
"clickCount":11,
"blogUrl":"https://blog.csdn.net/BiandanLoveyou/article/details/115710882"
}
POST /blog/info
{
"blogName":"Windows安装ElasticSearch、Kibana、Logstash(ELK)",
"blogDesc":"ElasticSearch是基于Java 语言开发的,因此需要JDK 环境",
"clickCount":22,
"blogUrl":"https://blog.csdn.net/BiandanLoveyou/article/details/115742897"
}
POST /blog/info
{
"blogName":"简单认识 Kibana 操作ElasticSearch,ElasticSearch 的版本控制",
"blogDesc":"一般来收,在读取数据较多的场景,使用乐观锁比较多,能提高吞吐量。在更新数据比较多的场景,使用悲观锁,能保证数据准确性。",
"clickCount":33,
"blogUrl":"https://blog.csdn.net/BiandanLoveyou/article/details/115772565"
}
POST /blog/info
{
"blogName":"SpringBoot(2.2.X) 整合 最新版 ElasticSearch(7.12.X版本)",
"blogDesc":"其实,我们可以把ElasticSearch设想成数据库!操作数据库的思想来操作即可。",
"clickCount":44,
"blogUrl":"https://blog.csdn.net/BiandanLoveyou/article/details/115773729"
}
POST /blog/info
{
"blogName":"Kibana高级查询语句、DSL语言查询和过滤、中文分词器",
"blogDesc":"Elasticsearch中默认的标准分词器对中文分词不是很友好,会将中文词语拆分成一个个中文的汉字。因此需要引入中文分词器 es-ik 插件",
"clickCount":55,
"blogUrl":"https://blog.csdn.net/BiandanLoveyou/article/details/115789099"
}
执行结果:
我们把执行五次的ID记录下来:
第1条:Bs9O6ngBioXCws8F2uB2
第2条:B89Q6ngBioXCws8FTuDJ
第3条:CM9Q6ngBioXCws8FcOCH
第4条:Cc9Q6ngBioXCws8FiOBV
第5条:Cs9Q6ngBioXCws8FoODN
接下来,我们使用 SpringBoot 代码整合查询 ElasticSearch。
entity 包下创建实体类:BlogEntity。(我们可以在IDEA 安装 lombok 插件,可以省略get、set 方法,在实体上标注 @Data 即可。为了演示方便,这里写 get、set 方法)
package com.study.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-04-19 下午 9:28
*/
@Document(indexName = "blog",type = "info")
public class BlogEntity {
@Id
private String id;//ES 的ID
private String blogName;//博客名称
private String blogDesc;//博客描述
private Integer clickCount;//点击数量
private String blogUrl;//博客地址
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getBlogName() {
return blogName;
}
public void setBlogName(String blogName) {
this.blogName = blogName;
}
public String getBlogDesc() {
return blogDesc;
}
public void setBlogDesc(String blogDesc) {
this.blogDesc = blogDesc;
}
public Integer getClickCount() {
return clickCount;
}
public void setClickCount(Integer clickCount) {
this.clickCount = clickCount;
}
public String getBlogUrl() {
return blogUrl;
}
public void setBlogUrl(String blogUrl) {
this.blogUrl = blogUrl;
}
}
dao 包下创建 BlogDao:注意,我们继承的是 org.springframework.data.elasticsearch.repository.ElasticsearchRepository
package com.study.dao;
import com.study.entity.BlogEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-04-16 下午 11:32
*/
public interface BlogDao extends ElasticsearchRepository<BlogEntity,String> {
}
编写 controller 层:
package com.study.controller;
import com.google.common.collect.Lists;
import com.study.dao.BlogDao;
import com.study.entity.BlogEntity;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-04-16 下午 11:33
*/
@RestController
@RequestMapping(value = "/blog")
public class BlogController {
@Autowired
private BlogDao blogDao;
/**
* 根据ID查询博客信息
*
* @param id
* @return
*/
@RequestMapping(value = "/findById", method = RequestMethod.GET)
public Optional<BlogEntity> findById(String id) {
Optional<BlogEntity> blogEntity = blogDao.findById(id);
return blogEntity;
}
/**
* 根据条件查询所有博客列表
* @param blogName 博客名称
* @param blogDesc 博客描述
* @param clickCount 点击数
* @return
*/
@RequestMapping(value = "/findAll", method = RequestMethod.POST)
public List<BlogEntity> findAll(String blogName,String blogDesc,Integer clickCount){
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//模糊查询博客名称
if(StringUtils.isNotBlank(blogName)){
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("blogName", blogName);
boolQueryBuilder.must(queryBuilder);
}
//模糊查询博客描述
if(StringUtils.isNotBlank(blogDesc)){
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("blogDesc", blogDesc);
boolQueryBuilder.must(queryBuilder);
}
//根据点击数精确查询
if(null != clickCount){
TermQueryBuilder queryBuilder = QueryBuilders.termQuery("clickCount", clickCount);
boolQueryBuilder.must(queryBuilder);
}
Iterable<BlogEntity> entities = blogDao.search(boolQueryBuilder);
//使用Google的工具包
List<BlogEntity> resultList = Lists.newArrayList(entities);
return resultList;
}
}
说明:我们使用的是单机版 ES,不使用集群版。学习的时候就学单机版好了。集群很耗电脑性能的。
OK,我们启动服务,使用 postman 做请求测试:先测试 findById 接口,这里的 id 值就是我们之前创建数据的 ID 值。
然后测试 findAll 接口:
1、测试只传递 blogName 的情况:
2、增加 clickCount 参数:字段之间,取的是 and
3、增加 blogDesc 字段的查询:博客内容是:需要JDK,而我们的请求参数是:安装JDK,但是ES依然帮我们查询出来,说明ES使用了分词检索功能。
OK,我们看下 ElasticSearch 如何做分页查询。
主要修改 controller 层,增加 findByPage 方法即可:引入 springframework.data 的相关接口
package com.study.controller;
import com.google.common.collect.Lists;
import com.study.dao.BlogDao;
import com.study.entity.BlogEntity;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-04-16 下午 11:33
*/
@RestController
@RequestMapping(value = "/blog")
public class BlogController {
@Autowired
private BlogDao blogDao;
/**
* 根据ID查询博客信息
*
* @param id
* @return
*/
@RequestMapping(value = "/findById", method = RequestMethod.GET)
public Optional<BlogEntity> findById(String id) {
Optional<BlogEntity> blogEntity = blogDao.findById(id);
return blogEntity;
}
/**
* 根据条件查询所有博客列表
*
* @param blogName 博客名称
* @param blogDesc 博客描述
* @param clickCount 点击数
* @return
*/
@RequestMapping(value = "/findAll", method = RequestMethod.POST)
public List<BlogEntity> findAll(String blogName, String blogDesc, Integer clickCount) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//模糊查询博客名称
if (StringUtils.isNotBlank(blogName)) {
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("blogName", blogName);
boolQueryBuilder.must(queryBuilder);
}
//模糊查询博客描述
if (StringUtils.isNotBlank(blogDesc)) {
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("blogDesc", blogDesc);
boolQueryBuilder.must(queryBuilder);
}
//根据点击数精确查询
if (null != clickCount) {
TermQueryBuilder queryBuilder = QueryBuilders.termQuery("clickCount", clickCount);
boolQueryBuilder.must(queryBuilder);
}
Iterable<BlogEntity> entities = blogDao.search(boolQueryBuilder);
//使用Google的工具包
List<BlogEntity> resultList = Lists.newArrayList(entities);
return resultList;
}
/**
* 分页查询
*
* @param blogName 博客名称
* @param blogDesc 博客描述
* @param clickCount 点击数量
* @param pageable 分页信息,我们可以设置默认值: page、value(每页查询数量)的值
* @return
*/
@RequestMapping(value = "/findByPage", method = RequestMethod.POST)
public Page<BlogEntity> findByPage(String blogName, String blogDesc, Integer clickCount, @PageableDefault(page = 0, value = 10) Pageable pageable) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//模糊查询博客名称
if (StringUtils.isNotBlank(blogName)) {
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("blogName", blogName);
boolQueryBuilder.must(queryBuilder);
}
//模糊查询博客描述
if (StringUtils.isNotBlank(blogDesc)) {
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("blogDesc", blogDesc);
boolQueryBuilder.must(queryBuilder);
}
//根据点击数精确查询
if (null != clickCount) {
TermQueryBuilder queryBuilder = QueryBuilders.termQuery("clickCount", clickCount);
boolQueryBuilder.must(queryBuilder);
}
Page<BlogEntity> search = blogDao.search(boolQueryBuilder, pageable);
return search;
}
}
重启服务,使用 postman 测试:
查询结果:
{
"content": [
{
"id": "Bs9O6ngBioXCws8F2uB2",
"blogName": "了解ElasticSearch",
"blogDesc": "这个系列教程我们主要学习ElasticSearch。去到大型互联网公司,这可是必备技能!",
"clickCount": 11,
"blogUrl": "https://blog.csdn.net/BiandanLoveyou/article/details/115710882"
},
{
"id": "CM9Q6ngBioXCws8FcOCH",
"blogName": "简单认识 Kibana 操作ElasticSearch,ElasticSearch 的版本控制",
"blogDesc": "一般来收,在读取数据较多的场景,使用乐观锁比较多,能提高吞吐量。在更新数据比较多的场景,使用悲观锁,能保证数据准确性。",
"clickCount": 33,
"blogUrl": "https://blog.csdn.net/BiandanLoveyou/article/details/115772565"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 0,
"pageSize": 2,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"facets": [],
"aggregations": null,
"scrollId": null,
"maxScore": 0.41181886,
"totalElements": 4,
"totalPages": 2,
"number": 0,
"size": 2,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"numberOfElements": 2,
"first": true,
"last": false,
"empty": false
}
默认 page 的第一页是 0,size 是每页查询数量。
返回的分页参数:totalElements是总数量,totalPages是总页数,number是当前页(从0开始计算),size是每页查询数量。
代码地址:https://pan.baidu.com/s/13QyQvO3mixrBL3Ubxh2RLA 提取码:fwfw