这一期我们用 Spring Boot + Spring Data Elasticsearch 整合 ES.
导入依赖
选用 Idea 快速创建 Spring Boot 项目,选择:
Spring Data Elasticsearch
Lombok
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 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-elasticsearch</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-devtools</artifactId > <scope > runtime</scope > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <optional > true</optional > </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 >
编写配置
不写也可以,因为这本来就是默认值。
1 spring.elasticsearch.rest.uris =http://127.0.0.1:9200
索引操作
到这里,我们就可以操作索引了。
通过 Autowired 自动注入 ElasticsearchRestTemplate
类,这里演示索引的创建、查看和删除:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Autowired private ElasticsearchRestTemplate elasticsearchRestTemplate;@Test public void testCreateIndex () { boolean isSuccess = elasticsearchRestTemplate.indexOps(IndexCoordinates.of("user" )).create(); System.out.println(isSuccess); } @Test public void testGetIndex () { Map<String, Object> map = elasticsearchRestTemplate.indexOps(IndexCoordinates.of("user" )).getMapping(); for (Map.Entry<String, Object> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } } @Test public void testDeleteIndex () { boolean isSuccess = elasticsearchRestTemplate.indexOps(IndexCoordinates.of("user" )).delete(); System.out.println(isSuccess); }
字段操作
创建实体类
@Document
指定索引名
@Field
指定字段类型,store 指定字段是否保存,analzyer
指定分词器。
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 @NoArgsConstructor @Getter @Setter @Accessors(chain = true) @Document(indexName = "user") public class User { @Id @Field(type = FieldType.Integer, store = true) private String id; @Field(type = FieldType.Keyword, store = true) private String name; @Field(type = FieldType.Integer, store = true) private int age; @Field(type = FieldType.Text, store = true, analyzer = "ik_max_word") private String desc; @Override public String toString () { return "Name: " + name + ", age: " + age + ", description: " + desc + "." ; } }
此处 ID 使用 String 类型,是因为当我们不指定 ID 时,String = null,ES
会自动生成随机 ID.
创建接口
Spring Data 系列的一贯传统,声明接口方法,自动通过动态代理实现。
至于接口方法的命名规则,可以查看我之前的 Spring Data JPA 笔记。
1 2 3 public interface UserDao extends ElasticsearchRepository <User, String> { List<User> findByDescMatches (String key) ; }
调用接口方法
我们来写一个 Service:
1 2 3 4 5 6 7 8 9 public interface ElasticsearchService { void addUser (User user) throws IOException; void addUserList (List<User> list) ; List<User> searchUserByDesc (String desc) throws IOException; }
实现类:
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 package org.koorye.service;import org.koorye.dao.UserDao;import org.koorye.entity.User;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Service public class ElasticsearchServiceImpl implements ElasticsearchService { @Autowired private UserDao userDao; @Override public void addUser (User user) { userDao.save(user); } @Override public void addUserList (List<User> list) { userDao.saveAll(list); } @Override public List<User> searchUserByDesc (String key) { return userDao.findByDescMatches(key); } }
非常简单,一行代码就可以解决一个方法。
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void testAddUser () { List<User> userList = new ArrayList <>(); userList.add(new User ().setName("koorye1" ).setAge(19 ).setDesc("I love python" )); userList.add(new User ().setName("koorye2" ).setAge(20 ).setDesc("I love java" )); userList.add(new User ().setName("koorye3" ).setAge(21 ).setDesc("I love c" )); elasticsearchService.addUserList(userList); } @Test public void testSearchUserByDesc () { String matchKey = "love" ; List<User> list = elasticsearchService.searchUserByDesc(matchKey); for (User user : list) { System.out.println(user); } }
返回结果:
1 2 3 Name: koorye1, age: 19, description: I love python. Name: koorye2, age: 20, description: I love java. Name: koorye3, age: 21, description: I love c.
复杂搜索
Spring Data Elasticsearch
的方法命名匹配使得我们执行复杂搜索变得异常简单。
官方文档的示例:
And
findByNameANdPrice
Or
findByNameOrPrice
Is
findByName
Not
findByNameNot
Between
findByPriceBetween
LessThan
findByPriceLessThan
LessThanEqual
findByPriceLessThanEqual
GreaterThan
findByPriceGreaterThan
GreaterThanEqual
findByPriceGreaterThanEqual
Before
findByPriceBefore
After
findByPriceAfter
Like
findByNameLike
StartingWith
findByNameStartingWith
EndingWith
findByNameEndingWith
Contains/Containing
findByNameContaining
In
findByNameIn(Collection<String>
names)
NotIn
findByNameNotIn(Collection<String>
names)
Near
findByStoreNear(暂不支持?)
True
findByAvailableTrue
False
findByAvailableFalse
OrderBy
findByAvailableTrueOrderByNameDesc
借助这些关键词,布尔查询、区间查询就可以非常方便的实现。
结果分页与排序
只需在参数中加入 Pageable 和 Sort 就可以实现,当然排序也可以使用
OrderBy :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface ElasticsearchService { void addUser (User user) throws IOException; void addUserList (List<User> list) ; List<User> searchUserByDesc (String desc) throws IOException; List<User> searchUserByDescOrderByAge (String desc) ; List<User> searchUserByDesc (String desc, Sort sort) ; Page<User> searchUserByDesc (String desc, Pageable pageable) throws IOException; }
Service 层代码就不再演示。
测试:
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 @Test public void testSearchUserBySort () { String key = "love" ; Sort sort = Sort.by(Sort.Direction.DESC, "age" ); List<User> userList = elasticsearchService.searchUserByDesc(key, sort); for (User user : userList) { System.out.println(user); } } @Test public void testSearchUserByOrder () { String key = "love" ; List<User> userList = elasticsearchService.searchUserByDescOrderByAge(key); for (User user : userList) { System.out.println(user); } } @Test public void testSearchUserByDescPageable () { String matchKey = "love" ; Pageable pageable = PageRequest.of(0 , 2 ); Page<User> userPage = elasticsearchService.searchUserByDesc(matchKey, pageable); List<User> userList = userPage.getContent(); for (User user : userList) { System.out.println(user); } }
返回结果:
1 2 3 4 5 6 Name: koorye3, age: 21, description: I love c. Name: koorye3, age: 21, description: I love c. Name: koorye2, age: 20, description: I love java. Name: koorye2, age: 20, description: I love java. Name: koorye1, age: 19, description: I love python. Name: koorye1, age: 19, description: I love python.
1 2 3 4 5 6 Name: koorye3, age: 21, description: I love c. Name: koorye3, age: 21, description: I love c. Name: koorye2, age: 20, description: I love java. Name: koorye2, age: 20, description: I love java. Name: koorye1, age: 19, description: I love python. Name: koorye1, age: 19, description: I love python.
1 2 3 Name: koorye1, age: 19, description: I love python. Name: koorye2, age: 20, description: I love java.
使用 @Query
@Query
注解使得我们可以使用 JSON 编写条件:
1 2 @Query("{\"match\": {\"desc\": \"love\"}}") List<User> searchUserByQuery () ;
测试:
1 2 3 4 5 6 7 @Test public void testSearchUserByQuery () { List<User> userList = elasticsearchService.searchUserByQuery(); for (User user : userList) { System.out.println(user); } }
返回结果:
1 2 3 4 5 6 Name: koorye1, age: 19 , description: I love python. Name: koorye2, age: 20 , description: I love java. Name: koorye3, age: 21 , description: I love c. Name: koorye1, age: 19 , description: I love python. Name: koorye2, age: 20 , description: I love java. Name: koorye3, age: 21 , description: I love c.
使用 NativeQuery
相当于原生的查询,我们可以指定很多复杂条件,如过滤、分页、排序、高亮等:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testSearchUserByNativeQuery () { NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder ().withFields("name" , "age" ) .withQuery(QueryBuilders.matchQuery("desc" , "love" )) .withPageable(PageRequest.of(0 , 3 )) .withSort(SortBuilders.fieldSort("age" ).order(SortOrder.DESC)) .withHighlightFields(new HighlightBuilder .Field("desc" )) .build(); List<User> userList = elasticsearchRestTemplate.queryForList(nativeSearchQuery, User.class, IndexCoordinates.of("user" )); for (User user : userList) { System.out.println(user); } }
返回结果:
1 2 3 Name: koorye3, age: 21, description: null. Name: koorye3, age: 21, description: null. Name: koorye2, age: 20, description: null.
最新版本中 NativeQuery 已被弃用?