碰到一点压力就把自己变成不堪重负的样子,碰到一点不确定性就把前途描绘成黯淡无光的样子,碰到一点不开心就把它搞得似乎是自己这辈子最黑暗的时候。
序
我们安装了Elasticsearch并且学会了它的各种查询,终归是要在项目中使用它。所以今天就创建一个项目,看一看Springboot是如何使用它的。
该项目代码放在:https://gitee.com/siumu/blog_code.git
准备工作
首先我们需要创建一个Springboot项目,非常的简单,也不需要什么额外的jar包,只需要把spring-boot-starter-data-elasticsearch
引入进来就可以了。我们如果了解过的话可能就知道,需要用Java High Level REST Client来对Elasticsearch进行操作,但是springboot很人性化的帮我们集成了这个东西,还封装了一下,让我们更加的容易使用。然后你还需要准备一个可以访问的Elasticsearch服务。
pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.2.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
这里还有一个问题就是版本问题
,我创建的springboot版本是2.3.5,它默认使用的Java High Level REST Client是7.6.2,但是我们的Elasticsearch是7.9.2,为了让他版本一致,我们只需要在POM文件中写上elasticsearch的版本就可以了
配置文件
spring:
elasticsearch:
rest:
uris:
- http://192.168.36.215:9200
写上你启动的Elasticsearch服务的地址,如此一来我们的准备工作就做完了,当然还要有数据,我们就用上篇博客的数据吧,方便点。有了数据,接下来的操作就很简单了,整一个实体类,然后使用spring-boot-starter-data-elasticsearch调用CRUD就可以了。
上手操作
建实体类:
/**
* @author xiumu
* @version 1.0
* @date 2020/11/7 19:12
*/
@Data
@Document(indexName = "xiumu_user")
public class UserInfo {
@Id
private String id;
/**
* 姓名
*/
private String username;
/**
* 年龄
*/
private Integer age;
/**
* 性别
*/
private Character gender;
/**
* 个性签名
*/
private String description;
}
这代码很简单吧,就像我们写实体类跟数据库映射一样,一模一样的操作。然后我们就像使用Jpa似的,创建一个repository
/**
* @author xiumu
* @version 1.0
* @date 2020/11/8 15:09
*/
@Repository
public interface UserInfoRepository extends ElasticsearchRepository<UserInfo, String> {
}
这样我们就可以使用它来进行简单的CRUD了。接下来我们就在测试类里边来使用。
@SpringBootTest
class SearchApplicationTests {
@Autowired
private UserInfoRepository userInfoRepository;
@Test
void contextLoads() {
Iterable<UserInfo> userInfos = userInfoRepository.findAll();
userInfos.forEach(System.out::println);
}
}
我们看到,它会给我们返回一个Iterable对象。而不是我们经常见到的List对象。测试结果如下:
虽然它默认给我们返回Iterable对象,但是如果我们想要它给我们返回List对象也是可以的,就像这样:
/**
* @author xiumu
* @version 1.0
* @date 2020/11/8 15:09
*/
@Repository
public interface UserInfoRepository extends ElasticsearchRepository<UserInfo, String> {
@Override
List<UserInfo> findAll();
}
我们直接把它给覆盖掉,这样再使用List来接收是完全没有问题的。
我们调用的时候发现其实它已经实现了很多方法:
然后我们要是根据字段来查询也是很方便,或者分页查询是非常的方便,就跟使用Jpa操作数据库一样方便。
/**
* @author xiumu
* @version 1.0
* @date 2020/11/8 15:09
*/
@Repository
public interface UserInfoRepository extends ElasticsearchRepository<UserInfo, String> {
@Override
List<UserInfo> findAll();
//根据名字查找
List<UserInfo> findByUsername(String username);
//根据年龄查找
List<UserInfo> findByAge(Integer age);
//根据名字或者个性签名查找
List<UserInfo> findByUsernameOrDescription(String username, String description);
//分页查询
@Override
Page<UserInfo> findAll(Pageable pageable);
//根据名字分页查询
Page<UserInfo> findByUsername(String username,Pageable pageable);
}
然后我们在测试类里边进行测试
@Test
void contextLoads() {
List<UserInfo> userInfos = userInfoRepository.findAll();
userInfos.forEach(System.out::println);
System.out.println("-------------------------------------");
userInfos = userInfoRepository.findByAge(20);
userInfos.forEach(System.out::println);
System.out.println("-------------------------------------");
userInfos = userInfoRepository.findByUsername("射手");
userInfos.forEach(System.out::println);
System.out.println("-------------------------------------");
userInfos = userInfoRepository.findByUsernameOrDescription("赵信","风一样的男人");
userInfos.forEach(System.out::println);
System.out.println("-------------------------------------");
Page<UserInfo> userInfos1 = userInfoRepository.findAll(PageRequest.of(0,5));
userInfos1.forEach(System.out::println);
}
测试结果如下:
需要注意的是分页查询要从第0页开始查起
。
除了这些还有很多查询方法,我们可以看它的官方文档,还是挺详细的。
ElasticsearchRestTemplate查询
有时候上边那个查询满足不了我们的需求,我们想要它返回更多原生的东西,也要自定义很多东西,那么我们就需要用到这个ElasticsearchRestTemplate查询了,这个东西能做更多的事情,也方便我们自定义一些查询操作。它用起来也非常的方便,我们只需要在用到它的类中将它注入进来即可。
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
那么我们就在测试类里直接使用。它同样封装了很多方法,我们先来看它的第一个查询方法
我们来看它的参数,Query,Class,IndexCoordinates,这不是一看就知道,Query就是查询条件嘛,Class应该就是返回对象的类型,IndexCoordinates就是索引呗。
IndexCoordinates类
我们先看这个IndexCoordinates类,直接看源码,这里构造函数被私有化,想要生成这个类的对象,就得调用这个of方法,把索引名字传进去,默认的type是"_doc"。而这个of方法可以传一个或者多个索引名称,岂不就是对应上了我们上个博客讲的多索引查询。
Query接口
接下来我们看这个查询条件Query接口,它有好几个实现类,理论上来说我们只需要用NativeSearchQuery
这个实现类就可以了,基本上你想要的的查询条件它都能生成。我们先看这个类的源码
一看它的构造器源码和参数,以及参数名称我们就立马知道,必然是使用了Builder设计模式,不知道大家有没有听说过这个建造者设计模式。这个呢先不管,我们还是直接说怎么生成查询条件吧。我们发现要想生成一个NativeSearchQuery的对象,必须要有QueryBuilder这个条件构造器
。
QueryBuilder对象
既然这个QueryBuilder是必须的,那我们就看看这个接口是什么东西吧。首先进入源码,看看它的实现类有哪些
我们看到它有很多实现类,我就找了这么几个我们常用的,看起来是不是很熟悉,就是构建我们经常使用的match查询,query_string查询,range查询等等
。每一种查询都被封装成类。当然,我们想要构建各种查询条件不能这么直接new这些对象,代码看起来不好看。所以它很方便的给我们提供了一个构建这些条件构造器的类QueryBuilders
。它就能帮我们创建这些对象。
开始使用
那么我们直接开始使用吧,眼睛见到的才会更直观。
@Test
void testElasticsearchRestTemplateDemo() {
//要查询的索引
IndexCoordinates xiumuUser = IndexCoordinates.of("xiumu_user");
//构建查询条件,查询全部
QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
Query query = new NativeSearchQuery(queryBuilder);
//执行查询
SearchHits<UserInfo> userInfoSearchHits = elasticsearchRestTemplate.search(query, UserInfo.class, xiumuUser);
//打印查询结果
for (SearchHit<UserInfo> searchHit : userInfoSearchHits.getSearchHits()) {
System.out.println(searchHit.getContent());
}
}
执行结果如下:
如图所示,确实查到了结果。我们看到,查询结果的返回类型是SearchHits
,有没有一点熟悉的名字,我们用kibana查询结果不也是叫hits吗:
SearchHits是一个接口,我们来看它的实现类里面的属性:
这不就跟我们用kibana查询的结果对应上了。到了这里我们基本已经知道了如何使用它,更加高级的功能还需要我们慢慢的摸索。
更复杂的查询
我们需要更加复杂的查询怎么构建呢?以下就是一些常用的查询方式:
//match查询age是20的条件
QueryBuilders.matchQuery("age",20);
//term查询age是20的条件
QueryBuilders.termQuery("age",20);
//terms查询age是20或者200,或者50的条件
QueryBuilders.termsQuery("age",20,200,50);
//query_string全文检索“xiumu”
QueryBuilders.queryStringQuery("xiumu");
//query_string检索username是“xiumu”
QueryBuilders.queryStringQuery("xiumu").field("username");
//multi_match查询字段username或者description的值是xiumu
QueryBuilders.multiMatchQuery("xiumu","username","description");
//range查询字段age的范围是在[18-200]之间
QueryBuilders.rangeQuery("age").gte(18).lte(200);
//exits查询字段age有值
QueryBuilders.existsQuery("age");
//wildcard查询字段description是以“男人”结尾的
QueryBuilders.wildcardQuery("description","*男人");
//bool查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//bool查询里添加must多个条件
boolQueryBuilder.must(QueryBuilders.termQuery("age",20));
boolQueryBuilder.must(QueryBuilders.existsQuery("age"));
boolQueryBuilder.must(QueryBuilders.wildcardQuery("description","*男人"));
//等等各种查询.......
结尾
Springboot操作Elasticsearch我们就这样简简单单的学会了,当然这是个刚刚入门到的基础,还有很多更复杂的操作需要摸索。
相关代码放在:https://gitee.com/siumu/blog_code.git