ElasticSearch之SpringDataElasticsearch操作

1. Spring Data ElasticSearch简介

1.1 什么是Spring Data

spring-boot-starter-data-redis > redisTemplate

Spring Data 是一个用于简化数据访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷。 Spring Data可以极大的简化数据操作的写法,可以在几乎不用写实现的情况下,实现对数据的访问和操作。除了CRUD外,还包括如分页、排序等一些常用的功能。

查看 Spring Data的官网:https://spring.io/projects/spring-data

1.2 什么是SpringDataES

SpringDataElasticsearch(以后简称SDE)是Spring Data项目下的一个子模块,是Spring提供的操作ElasticSearch的数据层,封装了大量的基础操作,通过它可以很方便的操作ElasticSearch的数据。
在这里插入图片描述
Spring Data 的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提 高开发效率。

包含很多不同数据操作的模块:

在这里插入图片描述

Spring Data Elasticsearch的页面:https://projects.spring.io/spring-data-elasticsearch/

在这里插入图片描述
特征:

  • 支持Spring的基于@Configuration的java配置方式,或者XML配置方式
  • 提供了用于操作ES的便捷工具类ElasticsearchTemplate。包括实现文档到POJO之间的自动智能映射。
  • 利用Spring的数据转换服务实现的功能丰富的对象映射
  • 基于注解的元数据映射方式,而且可扩展以支持更多不同的数据格式
  • 根据持久层接口自动生成对应实现方法,无需人工编写基本操作代码(类似mybatis,根据接口自动得到实现)。当然,也支持人工定制查询

2. 准备工作

2.1 启动elasticsearch服务

2.2 创建工程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
导入依赖
在这里插入图片描述
yml配置文件信息:

server:
  port: 8081

spring:
  application:
    name: spring-data-es
  data:
    elasticsearch:
      cluster-name: elasticsearch # 集群名称
      cluster-nodes: 127.0.0.1:9300 # 节点信息 tcp端口号

需要注意的是,SpringDataElasticsearch底层使用的不是Elasticsearch提供的RestHighLevelClient,而是TransportClient,并不采用Http协议通信,而是访问elasticsearch对外开放的tcp端口,ElasticSearch默认tcp端口。
另外,SpringBoot已经帮我们配置好了各种SDE配置,并且注册了一个ElasticsearchTemplate供我们使用。

3. 索引库的操作

3.1 创建实体类Goods

@Data
@AllArgsConstructor
@NoArgsConstructor
//集群时可以设置 : shards:分片数量(默认值:5),replicas:副本数量(默认值:1)
@Document(indexName = "goods", type = "goods")
public class Goods {


  /**
   * 必须有id,这里的id是全局唯一的标识,等同于es中的“_id”
   */
  @Id
  private Long id;

  /**
   * 标题
   * type: 字段数据类型
   * analyzer: 分词器类型
   * index: 是否索引(默认值:true)
   * store: 是否存储(默认值:false)
   */
  @Field(type = FieldType.Text,analyzer = "ik_max_word")
  private String title;

  /**
   * 分类
   */
  @Field(type = FieldType.Keyword)
  private String category;

  /**
   *    品牌
   */
  @Field(type = FieldType.Keyword)
  private String brand;

  /**
   *  价格
   */
  @Field(type = FieldType.Double)
  private Double price;

  /**
   * 图片地址
   */
  @Field(type = FieldType.Keyword,index = false)
  private String images;
}

几个用到的注解:

  • @Document:声明索引库配置
    • indexName:索引库名称
    • type:类型名称,默认是“docs”
    • shards:分片数量,默认5
    • replicas:副本数量,默认1
  • @Id:声明实体类的id
  • @Field:声明字段属性
    • type:字段的数据类型
    • analyzer:指定分词器类型
    • index:是否创建索引 默认为true
    • store:是否存储 默认为false

3.2 创建删除索引库以及类型映射

进入test文件夹下面进行单元测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringEs01ApplicationTests {


  @Autowired
  private ElasticsearchTemplate elasticsearchTemplate;

  //创建索引库
  @Test
  public void testCreateIndex(){

    boolean index = elasticsearchTemplate.createIndex(Goods.class);

    System.out.println("goods索引"+(index?"":"没有")+"创建成功");

  }

  //创建类型和映射
  @Test
  public void testPutMapping(){
    boolean flag = elasticsearchTemplate.putMapping(Goods.class);

    System.out.println("goods映射"+(flag?"":"没有")+"创建成功");
  }

  //删除索引库
  @Test
  public void testDeleteIndex(){

    boolean flag = elasticsearchTemplate.deleteIndex(Goods.class);

    System.out.println("删除索引库"+(flag?"成功":"失败"));
  }
}

4. 使用ElasticsearchRepository对数据进行增删改

4.1 新建 GoodRepository 接口

SDE的文档索引数据CRUD并没有封装在ElasticsearchTemplate中,而是有一个叫做ElasticsearchRepository的接口:


public interface GoodsEsRepository extends ElasticsearchRepository<Goods,Long> {

}

执行springboot的main方法, 发现创建了 索引库和映射type

4.2 创建单个文档

@RunWith(SpringRunner.class)
@SpringBootTest
public class GoodsESRepositoryTest {

    @Autowired
    private GoodsESRepository goodsESRepository;

    @Test
    public void demo01() {
        Goods goods = new Goods(1L, "华为手机1", "手机", "华为", 1999.0, "http://wwww.baidu.com");
        goodsESRepository.save(goods);
    }
}

4.3 批量创建文档

@Test
    public void demo02() {
        // 需求: 批量新建
        List<Goods> goodsList = new ArrayList<Goods>();

        for (long i = 2; i < 10 ; i++) {
            Goods goods = new Goods(i, "小米手机" + i, "手机", "小米", 1999.0 + 1000, "http://wwww.baidu.com");
            goodsList.add(goods);
        }
        goodsESRepository.saveAll(goodsList);
    }

4.4 修改和删除文档

 @Test
    public void demo05() {
        // 需求5: 修改
        // 1 先查
        Optional<Goods> optional = goodsRepository.findById(2L);
        if(optional.isPresent()) {
            Goods goods = optional.get();

            System.out.println("=========== " + goods);
            // 2 再改
            goods.setTitle(goods.getTitle() + "22222222222");
            goods.setPrice(300D);
            goodsRepository.save(goods);
        }
    }

    @Test
    public void demo06() {
        // 需求5: 删除
        goodsRepository.deleteById(9L);
    }

5 查询文档数据

默认提供了根据id查询, 查询所有两个功能

5.1. 根据id查询

  1. 调用goods仓库根据id查询
  2. 判断返回 Optional 对象是有有值
  3. 从Optional 对象中获取查询结果
 @Test
    public void demo03() {
        // 需求: 根据id查询商品信息
        Optional<Goods> optional = goodsESRepository.findById(6);
        if(optional.isPresent()) {
            Goods goods = optional.get();
            System.out.println(goods);
        }
    }

5.2. 查询所有

  @Test
    public void demo04() {
        // 需求: 查询所有
        Iterable<Goods> it = goodsESRepository.findAll();
        for (Goods goods : it) {
            System.out.println(goods);
        }
    }

5.3. 使用search查询

  1. 构建QueryBuilder 对象设置查询类型和查询条件
  2. 调用good仓库search方法进行查询
  3. 遍历打印输出查询结果
 @Test
    public void demo05() {
        // 需求: 查询title中含有华为的数据
        QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "华为");
        Iterable<Goods> it = goodsESRepository.search(queryBuilder);
        for (Goods goods : it) {
            System.out.println(goods);
        }
    }

5.4. 使用search查询并分页排序

  1. 构建Sort排序对象,指定排序字段和排序方式
  2. 使用PageRequest构建Pageable分页对象,指定分页参数,并将排序对象设置到分页对象中
  3. 调用goods仓库search方法进行查询
  4. 解析结果
@Test
public void demo06() {
    // 需求: 查询所有 且 按照id排序
    // 1. 构建Sort排序对象,指定排序字段和排序方式
    Sort sort = new Sort(Sort.Direction.ASC, "id");

    // 2. 使用PageRequest构建Pageable分页对象,指定分页参数,并将排序对象设置到分页对象中
    Pageable pageable = PageRequest.of(0, 3, sort);

    // 3. 调用goods仓库search方法进行查询
    Page<Goods> page = goodsESRepository.search(QueryBuilders.matchAllQuery(), pageable);

    // 4 解析结果
    // 4.1 获取总记录数
    long totalElements = page.getTotalElements();
    System.out.println("总记录数: ===== " + totalElements);

    // 4.2 获取总页数
    int totalPages = page.getTotalPages();
    System.out.println("总页数: ===== " + totalPages);

    // 4.2 获取当前页的数据
    List<Goods> goodsList = page.getContent();
    for (Goods goods : goodsList) {
        System.out.println("========== " + goods);
    }

}

6. 使用ElasticsearchTemplate查询

SDE也支持使用ElasticsearchTemplate进行原生查询

而查询条件的构建是通过一个名为NativeSearchQueryBuilder的类来完成的,不过这个类的底层还是使用的原生API中的QueryBuildersHighlightBuilders等工具。

6.1. 分页和排序

可以通过NativeSearchQueryBuilder类来构建分页和排序、聚合等操作

queryBuilder.withQuery() //设置查询类型和查询条件

queryBuilder.withPageable() //设置分页

queryBuilder.withSort()//设置排序

  1. 构建NativeSearchQueryBuilder查询对象
  2. 使用QueryBuilders指定查询类型和查询条件
  3. 使用SortBuilders指定排序字段和排序方式
  4. 使用PageRequest对象指定分页参数
  5. 调用NativeSearchQueryBuilder的build方法完成构建
  6. 使用ElasticsearchTemplate完成查询
  7. 解析结果
@RunWith(SpringRunner.class)
@SpringBootTest
public class GoodsESRepositoryTest {


    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 使用ElasticsearchTemplate完成分页排序查询
     */
    @Test
    public void nativeSearchQuery(){
        //1.构建NativeSearchQueryBuilder查询对象
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        //2.使用QueryBuilders指定查询类型和查询条件
        searchQueryBuilder.withQuery(QueryBuilders.matchQuery("title","小米"));
        //3.使用SortBuilders指定排序字段和排序方式
        searchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
        //4.使用PageRequest对象指定分页参数
        searchQueryBuilder.withPageable(PageRequest.of(0,2));
        //5.调用NativeSearchQueryBuilder的build方法完成构建
        NativeSearchQuery searchQuery = searchQueryBuilder.build();
        //6.使用ElasticsearchTemplate完成查询
        AggregatedPage<Goods> page = elasticsearchTemplate.queryForPage(searchQuery, Goods.class);
        //7.解析结果
        //7.1获取总记录数
        long totalElements = page.getTotalElements();
        System.out.println("totalElements: " + totalElements);
        //7.2获取页总数
        int totalPages = page.getTotalPages();
        System.out.println("totalPages: " + totalPages);
        //7.3遍历查询结果
        for (Goods goods : page) {
            System.out.println("结果 : " + goods);
        }
    }
}

6.2. 高亮显示

@Test
    public void demo05() {
        // 增加高亮显示
        //1. 构建NativeSearchQueryBuilder查询对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 8 高亮显示的效果
        nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("title").preTags("<span style='color:blue;'>").postTags("</span>"));
        //2. 使用QueryBuilders指定查询类型和查询条件
        nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("title", "华为"));
        //3. 使用SortBuilders指定排序字段和排序方式
        nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.ASC));
        //4. 使用PageRequest对象指定分页参数
        nativeSearchQueryBuilder.withPageable(PageRequest.of(1,3));
        //5. 调用NativeSearchQueryBuilder的build方法完成构建
        NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();
        //6. 使用ElasticsearchTemplate完成查询
        // 为了高亮显示 改造第6步
        AggregatedPage<Goods> page = elasticsearchTemplate.queryForPage(searchQuery, Goods.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                // 封装数据的对象
                List<Goods> goodsList = new ArrayList<Goods>();

                // 1 获取命中对象
                SearchHits hits = response.getHits();
                // 2 判断 校验是否为空,
                // 2.1 如果为空, 直接返回null; 否则 继续执行
                if(hits.getHits().length<=0) {
                    return null;
                }
                // 2.2 遍历
                for (SearchHit hit : hits) {
                    // 目标: 将查询结果封装goods对象中 再放到集合中
                    // 1 创建goods对象
                    Goods goods = new Goods();
                    // 2 设置内容
                    // 设置id
                    String id = hit.getId();
                    goods.setId(Long.parseLong(id));
                    // 设置分类属性
                    String category = hit.getSourceAsMap().get("category").toString();
                    if(category != null) {
                        goods.setCategory(category);
                    }
                    // 设置 brand 品牌属性
                    String brand = hit.getSourceAsMap().get("brand").toString();
                    if(brand != null) {
                        goods.setBrand(brand);
                    }

                    // 设置 brand 品牌属性
                    String images = hit.getSourceAsMap().get("images").toString();
                    if(images != null) {
                        goods.setImages(images);
                    }

                    // 设置价格
                    String price = hit.getSourceAsMap().get("price").toString();
                    if(price != null) {
                        goods.setPrice(Double.parseDouble(price));
                    }

                    // 设置高亮字段
                    if(hit.getHighlightFields().size()>0) {
                        String title = hit.getHighlightFields().get("title").fragments()[0].toString();
                        goods.setTitle(title);
                    }


                    // 3 将goods对象放到 集合中
                    goodsList.add(goods);

                }

                return new AggregatedPageImpl<>((List<T>) goodsList);
            }
        });
        //7. 解析结果
        // 7.1 获取总记录数
        System.out.println("总记录数 ====" + page.getTotalElements());
        // 7.2 获取总页数
        System.out.println("总页数 ====" + page.getTotalPages());
        // 7.3 获取每条记录信息
        // List<Goods> goodsList = page.getContent();
        for (Goods goods : page) {
            System.out.println("=====================");
            System.out.println(goods);
        }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值