SpringBoot中ElasticsearchRestTemplate的使用

3,130 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

SpringBoot中ElasticsearchRestTemplate的使用

环境: Java8 , SpringBoot 2.3.3.RELEASE, elasticsearch 7.6.2

准备

maven

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

yml

spring:
  elasticsearch:
    rest:
      uris: 127.0.0.1:9200

启动类添加@EnableElasticsearchRepositories(basePackages = {"com.xx.elasticsearch.repository"}) 开启repositories功能

doc

@Data
@Builder
@Document(indexName = "user", replicas = 0)
public class User {
    @Id
    private Long id;
    /**
     * 昵称
     */
    @MultiField(mainField = @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart"),
            otherFields = @InnerField(suffix = "keyword", type = FieldType.Keyword))
    private String nickname;
    /**
     * 账号
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 邮箱
     */
    private String email;
    /**
     * 头像
     */
    private String portrait;
    /**
     * 地址
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String address;

    /**
     * 个性签名
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String signature;
    /**
     * 状态, 0 停用, 1启用
     */
    @Field(type = FieldType.Integer)
    private Integer status;
    /**
     * 过期时间
     */
    private Long expire;
    /**
     * 创建时间
     */
    private Long createTime;

    /**
     * 更新时间
     */
    private Long updateTime;
}

Repository

public interface UserRepository extends ElasticsearchRepository<User, Long> {
}

测试使用

注入相关属性准备使用

 @Autowired
 private ElasticsearchRestTemplate elasticsearchTemplate;
 @Autowired
 private UserRepository userRepositories;

测试Repository方式操作文档

Repository操作有点类似于JPA

添加文档 执行下面的测试方法, 可以看到

  @Test
  void save() {
      User user = User.builder()
              .id(1L)
              .nickname("小明")
              .username("张三")
              .password("e10adc3949ba59abbe56e057f20f883e")
              .email("xiaoming@163.com")
              .address("浙江杭州拱墅区")
              .portrait("@666")
              .signature("美妙的人生,在于能够迷上什么!")
              .expire(4102415999L)
              .createTime(1629500572L)
              .updateTime(1629500637L)
              .status(1)
              .build();
      userRepositories.save(user);
      User user1 = User.builder()
              .id(2L)
              .nickname("小红")
              .username("李四")
              .password("e10adc3949ba59abbe56e057f20f883e")
              .email("xiaohong@163.com")
              .address("浙江杭州西湖区")
              .portrait("@333")
              .signature("弱小和无知不是生存的障碍, 傲慢才是!")
              .expire(4102415999L)
              .createTime(1629309372L)
              .updateTime(1629440205L)
              .status(1)
              .build();
      userRepositories.save(user1);
  }

查询

 @Test
 void query() {
     userRepositories.findAll().forEach(System.out::println);
 }

删除

 @Test
 void delete() {
     // userRepositories.deleteById(1L);
     userRepositories.deleteAll();
 }

ElasticsearchRestTemplate

添加

 User user = User.builder()
         .id(3L)
         .nickname("小华")
         .username("赵六")
         .password("e10adc3949ba59abbe56e057f20f883e")
         .email("xiaoming@163.com")
         .address("浙江杭州拱墅区")
         .portrait("@666")
         .signature("美妙的人生,在于能够迷上什么!")
         .expire(4102415999L)
         .createTime(1629500572L)
         .updateTime(1629500637L)
         .status(1)
         .build();
 // 使用对象添加文档
 // elasticsearchTemplate.save(user);
 IndexQuery indexQuery = new IndexQuery();
 indexQuery.setObject(user);
 IndexQuery indexQuery1 = new IndexQuery();
 user.setId(4L);
 indexQuery1.setObject(user);
 List<IndexQuery> indexQueries = new ArrayList<>();
 indexQueries.add(indexQuery);
 indexQueries.add(indexQuery1);
// 批量添加或更新文档
 elasticsearchTemplate.bulkIndex(indexQueries, IndexCoordinates.of("user"));
// 批量更新数据
// List<UpdateQuery> updateQueries = new ArrayList<>();
// elasticsearchTemplate.bulkUpdate(updateQueries, IndexCoordinates.of("user"));

查询

  @Test
  void queryByTemplate() {
      String queryColumn = "signature";
      BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
      queryBuilder
              .must(QueryBuilders.matchPhraseQuery(queryColumn, "弱小"))
      // 过滤
      // .filter()
      ;
      // 高亮处理, 其实就是拼接<span/>标签, 浏览器会自动解析该标签
      HighlightBuilder.Field highlightField = new HighlightBuilder.Field(queryColumn)
	      // 设置为red
              .preTags("<span style=\"color:red\">")
              .postTags("</span>");
      Query query = new NativeSearchQueryBuilder()
              .withQuery(queryBuilder)
              // post_filter, 查询后过滤结果
              // .withFilter(queryBuilder)
              // 分页
              .withPageable(PageRequest.of(0, 10))
              // 返回字段
              // .withFields()
              // 排序
              // .withSort()
              // 聚合
              // .addAggregation()
              // 高亮
              .withHighlightFields(highlightField)
              .build();
      SearchHits<User> search = elasticsearchTemplate.search(query, User.class);
      if (search.getTotalHits() > 0) {
          List<User> users = new ArrayList<>();
          search.forEach(userSearchHit -> {
              User user = userSearchHit.getContent();
              Map<String, List<String>> highlightFields = userSearchHit.getHighlightFields();
              if (highlightFields.size() > 0) {
		  // 将高亮的数据结果填充到响应数据中
                  List<String> strings = highlightFields.get(queryColumn);
                  if (CollUtil.isNotEmpty(strings)) {
                      user.setSignature(strings.get(0));
                  }
              }
              users.add(user);
          });
          users.forEach(System.out::println);
      }
  }

注意: 这里有个比较坑的点, 这个withFilter设置过滤, 我们当时在老的项目中有使用withFilter进行查询的, 发现执行查询的耗时很长, 然后断点跟踪到源码中发现了这个withFilter到org.springframework.data.elasticsearch.core.RequestFactory#searchRequest这里 被设置成了postFilter, 也就是先查询出结果, 然后再过滤, 分析我们的需求, 肯定是查询时就过滤