【Spring Boot】Spring Boot之使用 Spring Data Elasticsearch 整合elasticsearch

一、Spring Data Elasticsearch简单介绍

  Spring Data Elasticsearch项目将核心Spring概念应用于使用Elasticsearch搜索引擎开发解决方案。我们提供了一个“模板”作为存储、查询、排序和划分文档的高级抽象。您将注意到Spring Framewor与Spring data solr和mongodb支持的相似之处

 GitHub地址:https://github.com/zhangboqing/spring-boot-demo-elasticsearch

 使用的spring boot版本:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

二、整合步骤

1)maven依赖

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

2)application.yml

spring:
  elasticsearch:
    rest:
      uris:
        - http://localhost:9200
      username: elastic
      password: 123456

3)定义实体,一个实体对于一个index

/**
 * @author zhangboqing
 * @date 2019-12-06
 */
public interface EsConsts {

    /**
     * 所以index都使用该类型名称
     */
    String DEFAULT_TYPE_NAME = "_doc";

    /**
     * 索引名称
     */
    String INDEX_NAME_MERCURY = "goods";

}
/**
 * @author zhangboqing
 * @date 2019-12-06
 */
@Document(indexName = EsConsts.INDEX_NAME_MERCURY, type = EsConsts.DEFAULT_TYPE_NAME, shards = 1, replicas = 0)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GoodsESEntity {
    // 筛选条件包括:商品名称,品牌,规格,适用车型,商品编号,原厂编号

    /**
     * 主键,商品ID
     */
    @Id
    private Long goodsId;

    /**
     * 商品名称
     */
    @Field(type = FieldType.Keyword)
    private String goodsName;
    /**
     * 品牌
     */
    @Field(type = FieldType.Keyword)
    private String goodBrand;
    /**
     * 规格
     */
    @Field(type = FieldType.Keyword)
    private String goodsSpec;
    /**
     * 商品编号
     */
    @Field(type = FieldType.Keyword)
    private String goodsAccessoriesCode;
    /**
     * 原厂编号
     */
    @Field(type = FieldType.Keyword)
    private String goodsOriginalFactoryCode;

    /**
     * 复合字段,会被分词后存储
     */
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    private String groupData;
}

4)定义Repository

/**
 * @author zhangboqing
 * @date 2019-12-06
 */
public interface GoodsESRepository extends ElasticsearchRepository<GoodsESEntity, Long> {

    /**
     * 根据goodsId区间查询
     */
    List<GoodsESEntity> findByGoodsIdBetween(Integer min, Integer max);

}

 

三、测试

/**
 * @Author zhangboqing
 * @Date 2020-01-03
 */
@SpringBootTest
@Slf4j
public class GoodsESRepositoryTest {

    @Autowired
    private GoodsESRepository goodsESRepository;

    /**
     * 测试新增
     */
    @Test
    public void save() {
        GoodsESEntity goodsESEntity1 = new GoodsESEntity(2L, "尾灯 L 大众速腾", "国际品牌", "16D 945 095", "31231231", "16D 945 095",
                "尾灯 L 大众速腾 国际品牌 16D 945 095 31231231 16D 945 095");
        GoodsESEntity goodsESEntity2 = new GoodsESEntity(1L, "", "国际品牌", "16D 945 095", "31231231", "16D 945 095",
                "后 国际品牌 16D 945 095 31231231 16D 945 095");
        Iterable<GoodsESEntity> goodsESEntities = goodsESRepository.saveAll(Arrays.asList(goodsESEntity1, goodsESEntity2));
        log.info("【save】= {}", goodsESEntities);
    }


    /**
     * 测试更新
     */
    @Test
    public void update() {
        goodsESRepository.findById(1L).ifPresent(goodsESEntity -> {
            goodsESEntity.setGoodsName(goodsESEntity.getGoodsName() + "\n更新更新更新更新更新");
            GoodsESEntity save = goodsESRepository.save(goodsESEntity);
            log.info("【save】= {}", save);
        });
    }

    /**
     * 测试删除
     */
    @Test
    public void delete() {
        // 主键删除
        goodsESRepository.deleteById(1L);

        // 对象删除
        goodsESRepository.findById(2L).ifPresent(goodsESEntity -> goodsESRepository.delete(goodsESEntity));

        // 批量删除
        goodsESRepository.deleteAll(goodsESRepository.findAll());
    }

    /**
     * 测试普通查询,按goodsId倒序
     */
    @Test
    public void select() {
        goodsESRepository.findAll(Sort.by(Sort.Direction.DESC, "goodsId"))
                .forEach(goodsESEntity -> log.info("【goods】: {}", JSON.toJSONString(goodsESEntity)));
    }

    /**
     * 自定义查询,根据goodsId范围查询
     */
    @Test
    public void customSelectRangeOfAge() {
        goodsESRepository.findByGoodsIdBetween(1, 2).forEach(goodsESEntity -> log.info("【goods】: {}", JSON.toJSONString(goodsESEntity)));
    }

    /**
     * 高级查询
     */
    @Test
    public void advanceSelect() {
        // QueryBuilders 提供了很多静态方法,可以实现大部分查询条件的封装
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("goodsName", "保时捷跑车V20");
        log.info("【queryBuilder】= {}", queryBuilder.toString());

        goodsESRepository.search(queryBuilder).forEach(goodsESEntity -> log.info("【goods】: {}", JSON.toJSONString(goodsESEntity)));
    }

    /**
     * 自定义高级查询
     */
    @Test
    public void customAdvanceSelect() {
        // 构造查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 添加基本的分词条件
        queryBuilder.withQuery(QueryBuilders.matchQuery("goodsName", "保时捷跑车V20"));
        // 排序条件
        queryBuilder.withSort(SortBuilders.fieldSort("goodsId").order(SortOrder.DESC));
        // 分页条件
        queryBuilder.withPageable(PageRequest.of(0, 2));
        Page<GoodsESEntity> goodsESEntities = goodsESRepository.search(queryBuilder.build());
        log.info("【people】总条数 = {}", goodsESEntities.getTotalElements());
        log.info("【people】总页数 = {}", goodsESEntities.getTotalPages());
        goodsESEntities.forEach(goodsESEntity -> log.info("【goods】= {}",JSON.toJSONString(goodsESEntity)));
    }

    /**
     * 测试聚合,测试平均goodsId
     */
    @Test
    public void avg() {
        // 构造查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 不查询任何结果
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));

        // 平均goodsId
        queryBuilder.addAggregation(AggregationBuilders.avg("goodsIdAvg").field("goodsId"));

        log.info("【queryBuilder】= {}", JSON.toJSONString(queryBuilder.build()));

        AggregatedPage<GoodsESEntity> goodsESEntities = (AggregatedPage<GoodsESEntity>) goodsESRepository.search(queryBuilder.build());
        double avgGoodsId = ((InternalAvg) goodsESEntities.getAggregation("goodsIdAvg")).getValue();
        log.info("【avgGoodsId】= {}", avgGoodsId);
    }

    /**
     * 测试高级聚合查询
     */
    @Test
    public void advanceAgg() {
        // 构造查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 不查询任何结果
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));

        // 1. 添加一个新的聚合,聚合类型为terms,聚合名称为goodsName,聚合字段为goodsId
        queryBuilder.addAggregation(AggregationBuilders.terms("goodsName").field("goodsName")
                // 2. 在goodsName聚合桶内进行嵌套聚合,求平均goodsId
                .subAggregation(AggregationBuilders.avg("goodsIdAvg").field("goodsId")));

        log.info("【queryBuilder】= {}", JSON.toJSONString(queryBuilder.build()));

        // 3. 查询
        AggregatedPage<GoodsESEntity> people = (AggregatedPage<GoodsESEntity>) goodsESRepository.search(queryBuilder.build());

        // 4. 解析
        // 4.1. 从结果中取出名为 goodsName 的那个聚合,因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
        StringTerms goodsName = (StringTerms) people.getAggregation("goodsName");
        // 4.2. 获取桶
        List<StringTerms.Bucket> buckets = goodsName.getBuckets();
        for (StringTerms.Bucket bucket : buckets) {
            // 4.3. 获取桶中的key,即goodsName名称  4.4. 获取桶中的文档数量
            log.info("{} 总共有 {} 个", bucket.getKeyAsString(), bucket.getDocCount());
            // 4.5. 获取子聚合结果:
            InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("goodsIdAvg");
            log.info("平均goodsId:{}", avg);
        }
    }

}

 

posted @ 2020-01-03 23:16  N!CE波  阅读(3314)  评论(0编辑  收藏  举报