【Elasticsearch 自学笔记三】Spring Boot 整合 ES

这一期我们用 Spring Boot + Spring Data Elasticsearch 整合 ES.

导入依赖

选用 Idea 快速创建 Spring Boot 项目,选择:

  • Spring Data Elasticsearch
  • Lombok
    <!-- Spring Data Elasticsearch -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>

    <!-- Spring Web -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Development -->
    <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>

    <!-- Lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>

    <!-- Spring Test -->
    <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>

编写配置

不写也可以,因为这本来就是默认值。

spring.elasticsearch.rest.uris=http://127.0.0.1:9200

索引操作

到这里,我们就可以操作索引了。

通过 Autowired 自动注入 ElasticsearchRestTemplate 类,这里演示索引的创建、查看和删除:

  @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 指定分词器。

@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 笔记。

public interface UserDao extends ElasticsearchRepository<User, String> {
  List<User> findByDescMatches(String key);
}

调用接口方法

我们来写一个 Service:

public interface ElasticsearchService {

  void addUser(User user) throws IOException;

  void addUserList(List<User> list);

  List<User> searchUserByDesc(String desc) throws IOException;

}

实现类:

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);
  }

}

非常简单,一行代码就可以解决一个方法。

测试:

  @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);
    }
  }

返回结果:

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 的方法命名匹配使得我们执行复杂搜索变得异常简单。

官方文档的示例:

关键词示例
AndfindByNameANdPrice
OrfindByNameOrPrice
IsfindByName
NotfindByNameNot
BetweenfindByPriceBetween
LessThanfindByPriceLessThan
LessThanEqualfindByPriceLessThanEqual
GreaterThanfindByPriceGreaterThan
GreaterThanEqualfindByPriceGreaterThanEqual
BeforefindByPriceBefore
AfterfindByPriceAfter
LikefindByNameLike
StartingWithfindByNameStartingWith
EndingWithfindByNameEndingWith
Contains/ContainingfindByNameContaining
InfindByNameIn(Collection<String> names)
NotInfindByNameNotIn(Collection<String> names)
NearfindByStoreNear(暂不支持?)
TruefindByAvailableTrue
FalsefindByAvailableFalse
OrderByfindByAvailableTrueOrderByNameDesc

借助这些关键词,布尔查询、区间查询就可以非常方便的实现。

结果分页与排序

只需在参数中加入 Pageable 和 Sort 就可以实现,当然排序也可以使用 OrderBy :

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 层代码就不再演示。

测试:

  @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);
    }
  }

返回结果:

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.
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.
Name: koorye1, age: 19, description: I love python.
Name: koorye2, age: 20, description: I love java.

使用 @Query

@Query 注解使得我们可以使用 JSON 编写条件:

  @Query("{\"match\": {\"desc\": \"love\"}}")
  List<User> searchUserByQuery();

测试:

  @Test
  public void testSearchUserByQuery() {
    List<User> userList = elasticsearchService.searchUserByQuery();
    for (User user : userList) {
      System.out.println(user);
    }
  }

返回结果:

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

相当于原生的查询,我们可以指定很多复杂条件,如过滤、分页、排序、高亮等:

  @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);
    }
  }

返回结果:

Name: koorye3, age: 21, description: null.
Name: koorye3, age: 21, description: null.
Name: koorye2, age: 20, description: null.

最新版本中 NativeQuery 已被弃用?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值