初探 Spring Data Elasticsearch

Spring Data Elasticsearch 介绍

Spring Data Elasticsearch 基于 spring data API 简化 Elasticsearch 操作,将原始操作 Elasticsearch 的客户端 API 进行封装 。Spring Data 为 Elasticsearch 项目提供集成搜索引擎。

Spring Data Elasticsearch POJO 的关键功能区域为中心的模型与 Elastichsearch 交互文档和轻松地编写一个存储索引库数据访问层。

官网:Spring Data Elasticsearch

1.引入依赖并配置地址

① 在 SpringBoot 项目中引入 Spring Data Elasticsearch 的依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

② 在 application.yml 中配置 Elasticsearch 的地址

1
2
3
spring:
elasticsearch:
uris: http://192.168.10.155:9200

2.创建实体类以及对应的文档 Document 关系

① 创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
* @author niumazlb
* @create 2022-08-30 19:41
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "langbei")
@Setting(shards = 1,replicas = 1)
public class UserVo {

private static final long serialVersionUID = 1L;
/**
* 用户id
*/
@Id
private long id;
/**
* 用户昵称
*/
@Field(name = "username")
private String username;
/**
* 用户账号
*/
@Field(name = "userAccount",type = FieldType.Text,searchAnalyzer="ik_max_word",analyzer="ik_max_word")
private String userAccount;
/**
* 用户头像
*/
@Field(name = "avatarUrl")
private String avatarUrl;
/**
* 性别
*/
@Field(name = "gender")
private Integer gender;
/**
* 标签列表
*/
@Field(name = "tags")
private String tags;
/**
* 电话
*/
@Field(name = "phone")
private String phone;
/**
* 联系方式
*/
@Field(name = "contactInfo")
private String contactInfo;
/**
* 个人简介
*/
@Field(name = "profile")
private String profile;
/**
* 用户状态
*/
@Field(name = "userStatus")
private Integer userStatus;
/**
* 创建时间
*/
@Field(name = "createTime")
private Date createTime;
/**
* 更新时间
*/
@Field(name = "updateTime")
private Date updateTime;
/**
* 是否删除
*/
@Field(name = "isDelete")
private Integer isDelete;
/**
* 用户角色 0-普通用户 1-管理员
*/
@Field(name = "userRole")
private Integer userRole;
/**
* 编号
*/
@Field(name = "planetCode")
private String planetCode;

}

注解介绍

@Document(indexName = “”) :

  • 指定索引库的名字,网上大多会在此注解中配置分片数、副本数,但是观察源码会发现这两个属性已经被废弃

image-20230221220406853

@Setting(shards = 1,replicas = 1) :

  • 用于声明索引库的配置,例如分片数、副本数等

image-20230221220653153

@Id,@Field(name = “”) :

  • @Id 用于声明 mapper 的_id,必须声明
  • @Field(..) 用于声明该字段的各个属性,比如字段名,字段类型,使用什么分词器,格式等
    • image-20230221221035647

3.创建文档的管理 ElasticSearchRepository 接口

① 创建 UserVoRepository

  • 继承 ElasticsearchRepository<**映射实体类**,**主键数据类型**>,里面有一些自带的方法
    • 新增、更新数据:使用 repository 的 save()方法实现
    • 删除数据:deleteById()、deleteAll()
    • 查询:findById()、findAll()
  • 自定义查询方法
    • 方法名满足指定格式,就可以自定义查询方法
      • findBy{Titlte}And{contnet}(String title,String contnet);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author niuma
* @create 2023-02-21 21:07
*/
@Repository
public interface UserVoRepository extends ElasticsearchRepository<UserVo,Long> {
/**
* 满足规定格式,可以自定义查询方法
* findBy{Titlte}And{contnet}(String title,String contnet)
* @param userAccount
* @return
*/
List<UserVo> findByUserAccount(String userAccount);
}

4.创建 service

UserVoService.java 几个简单的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author niumazlb
*/
public interface UserVoService {
List<UserVo> findByUserAccount(String userAccount);

void save(UserVo docBean);

void saveAll(List<UserVo> list);

Iterator<UserVo> findAll();
}

UserVoServiceImpl.java 实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @author niumazlb
*/
@Service
public class UserVoServiceImpl implements UserVoService {

@Resource
private UserVoRepository elasticRepository;

@Override
public List<UserVo> findByUserAccount(String userAccount) {
return elasticRepository.findByUserAccount(userAccount);
}

@Override
public void save(UserVo docBean) {
elasticRepository.save(docBean);
}

@Override
public void saveAll(List<UserVo> list) {
elasticRepository.saveAll(list);
}

@Override
public Iterator<UserVo> findAll() {
return elasticRepository.findAll().iterator();
}
}

5.简单测试

  • 依次执行可以得到正确的结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* @author niuma
* @create 2023-02-21 21:11
*/
@SpringBootTest
class UserVoServiceTest {
@Resource
UserVoService userVoService;
@Resource
UserService userService;

/**
* 创建索引库以及 mapping
*/
@Test
void saveAll() {
//从数据库读取数据并加入list
List<UserVo> list = userService.list().stream().map(user -> {
UserVo userVo = new UserVo();
BeanUtils.copyProperties(user, userVo);
return userVo;
}).collect(Collectors.toList());
//批量加入文档
userVoService.saveAll(list);
}

@Test
void findAll() {
Iterator<UserVo> all = userVoService.findAll();
while(all.hasNext()){
UserVo next = all.next();
System.out.println(next);
}
}

@Test
void findByUserAccount() {
List<UserVo> userVos = userVoService.findByUserAccount("4");
System.out.println(userVos);
}
}

6.复杂查询

参考:SpringDataElasticSearch 框架 - 掘金 (juejin.cn)

复杂查询需要使用 ElasticsearchRestTemplate对象

① 创建索引库以及 mapping 的方法

1
2
3
4
5
6
7
//创建索引库
template.indexOps(UserVo.class).create();//从对象中创建
template.indexOps(IndexCoordinates.of("langbei")).create();//指定名称创建

//创建mapping
Document mapping = template.indexOps(UserVo.class).createMapping();
template.indexOps(UserVo.class).putMapping(mapping);

② 查询

  1. 使用 NativeSearchQuery 封装查询条件
  2. SearchHits searchHits = template.search(Query query, Class<T> clazz); 执行查询并得到查询结果
  3. 对结果处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Resource
ElasticsearchRestTemplate template;

@Test
void testElasticsearchRestTemplate(){
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("userAccount","4")) //查询条件
.withHighlightBuilder(new HighlightBuilder().field("userAccount").preTags("<em>").postTags("</em>")) //高亮设置前后缀
.build();
SearchHits<UserVo> searchHits = template.search(query, UserVo.class); //执行查询
long totalHits = searchHits.getTotalHits(); //获取查询到的条数
System.out.println("totalHits = " + totalHits);
List<SearchHit<UserVo>> searchHits1 = searchHits.getSearchHits();
searchHits1.forEach( e->{
UserVo content = e.getContent();
System.out.println("content = " + content);
Map<String, List<String>> highlightFields = e.getHighlightFields();
List<String> highlightUserAccount = highlightFields.get("userAccount");
System.out.println(highlightUserAccount);
});
}

image-20230221224835407