设计方案

ElasticSearch(over) - Spring Dat

2020-04-26  本文已影响0人  秃头猿猿

1.Spring Data ElasticSeach

1.1 前言

es本身提供java客户端去操作es,但是有一些缺点:

同时 现在很多开发都是基于SpringBoot,ES提供的客户端我们还需要手动去整合到SpringBoot项目中去,这是非常麻烦的。所以我们可以采用 Spring Data ElasticSearch.

1.2 介绍

大体意思如下:

Spring Data的任务是为数据访问提供一个熟悉的、一致的、基于Spring的编程模型,同时仍然保留底层数据存储的特殊特性。

它使使用数据访问技术、关系数据库和非关系数据库、map-reduce框架以及基于云的数据服务变得容易。这是一个伞形项目,包含许多特定于给定数据库的子项目。这些项目是通过与这些令人兴奋的技术背后的许多公司和开发人员合作开发的。

大体意思如下:

用于Elasticsearch的Spring数据是伞形Spring数据项目的一部分,该项目旨在为新的数据存储提供一个熟悉且一致的基于Spring的编程模型,同时保留存储特定的特性和功能。

Spring Data Elasticsearch项目提供了与Elasticsearch搜索引擎的集成。Spring Data Elasticsearch的关键功能区域是一个以POJO为中心的模型,用于与Elastichsearch文档交互并轻松编写存储库样式的数据访问层。

大体意思如下:

Spring配置支持使用基于Java的@configuration类或用于ES客户机实例的XML命名空间。

ElasticsearchTemplate帮助程序类,可提高执行常见ES操作的效率。包括文档和POJO之间的集成对象映射。

与Spring转换服务集成的功能丰富的对象映射

基于注释的映射元数据,但可扩展以支持其他元数据格式

存储库接口的自动实现,包括对自定义查找器方法的支持。

对存储库的CDI支持

总结:

以前繁琐的操作,如果引入spring data elasticsearch后将变得非常进键。

1.3 版本问题

由于不同spring data elasticsearch 对 es的支持也不一样。我们在先前进行api操作时,使用的es版本是6.2.4,

如果我们采用spring data elasticsearch 最新的 版本去测试学习,可能就对这个es版本不支持了。

所以,这次我们采用的springboot版本为 2.1.11.RELEASE

1.4 编码实现

代码同步在这个地址: https://github.com/smallCodeWangzh/es-demo.git

1.4.1 创建项目

image-20200426140957772.png image-20200426140848913.png

1.4.2 pom.xml 修改版本

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.11.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.elasticsearch</groupId>
    <artifactId>es-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>es-demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.4.3 增加配置

修改application.yml配置。增加es配置条件

spring:
  data:
    elasticsearch:
      cluster-name: docker-cluster
      cluster-nodes: 192.168.169.130:9300

注意:

​ 这里的端口是9300而不是9200

​ java客户端默认访问的集群客户端

1.4.4 新建一个POJO

package com.elasticsearch.bean;

import lombok.Data;

@Data
public class Goods {
    /**
     *  编号
     */
    private int id;
    /**
     *  标题
     */
    private String title;
    /**
     * 价格
     */
    private double price;
    /**
     * 品牌
     */
    private String  brand;

    /**
     * 封面图片地址
     */
    private String images;
}

1.4.5 添加ES配置

SpringData ElasticSearch 通过在POJO类加上一些注解来与索引进行绑定。这跟Spring Data JPA非常类型

1.5 测试

1.5.1 模板对象

image-20200426145945836.png
image-20200426165242680.png

这个对象封装了大量的对es操作。

1.5.2 创建索引

1.5.3 创建映射

    @Test
    public void createMapping() {
        elasticsearchTemplate.putMapping(Goods.class);
    }
image-20200426172656080.png

1.5.4 删除索引

    @Test
    public void deleteMapping() {
        elasticsearchTemplate.deleteIndex(Goods.class);
    }
image-20200426172820153.png

1.5.5 文档操作(CRUD)

1.5.5.1 前言

熟悉Spring Data JPA就知道,当对数据库进行增删改查时,我们不需要写任何的接口处理。会自动根据方法和POJO类信息去进行CRUD。

同样的 Spring Data ElasticSearch也是一样,也需要继承一个接口,就可以实现基本的CRUD了.这个接口

就是ElasticsearchRepossitory

1.5.5.2 ElasticsearchRepository

从上图源码我们可以看出,ElasticsearchRepository 继承了ElasticsearchCrudRepository,同时自己还定义了一些方法。具体的方法我们稍后再说。我们接着看继承关系

从上图源码可知,PagingAndSortingRepository继承了CrudRepository

从上图源码可知,CrudRepository继承Repository

从上图源码中看出 ,这个接口没有继承任何接口,也就是这个接口是一个顶级接口。

从图中的继承关系看出,ElasticsearchRepository处于最底层,继承了上面接口所有的方法。因此当我们自己去写Dao层接口时,继承ElasticsearchRepository即可。

1.5.5.4 新增文档

注意:_id_source里面的id保持一致的

1.5.5.5 批量新增

image-20200427094128063.png

通过kibana查询

image-20200427094230113.png

1.5.5.6 修改文档

修改与新增的方法是一致的,原理就是id存在就修改,不存在就新增

image-20200427094418062.png

通过kibana查询

image-20200427094511128.png

1.5.5.7 基本查询

默认提供一些查询所有的方法。同时有的方法还提供了排序的功能。

为了测试出更加直观的的效果,在POJO类加上toString方法

image-20200427095104970.png

运行测试代码

image-20200427095155919.png

手动指定以价格降序排序

image-20200427095421555.png

分页查询,同时还指定排序。

image-20200427095830103.png

1.5.5.8 自定义查询

1.5.5.8.1 介绍

在刚刚的例子中我们基本上使用的是默认给我提供的方法去进行操作,但有时候这些方法并不能满足我们的要求,此时我们就需要自定义方法(规则与Spring Data JPA相同)

GoodsRespoitry接口自定义方法,这个方法只要满足一定的规则,开发人员不需要实现,框架就会帮我们去实现。

规则如下:

官方文档地址: https://docs.spring.io/spring-data/elasticsearch/docs/3.1.14.RELEASE/reference/html/#repositories.query-methods.details

同时Spring Data ElasticSearch 支持源生的查询

image-20200427101040076.png

1.5.5.8.2 测试查询

当我们的title是 苹果荣耀,发现我们查询不到数据:

image-20200427103935006.png

这是因为当我们去进行查询时默认是and查询,同时在实际开发过程中,有些复杂查询这种自定义方法实现不了,例如聚合。但是一些简单的查询可以这样去实现。

那么复杂的查询的实现不了,那么我们该使用什么去进行查询呢。此时我们就需要使用ElasticSearch原生查询。

1.5.5.8.3 高级查询

1. QueryBuilder

通过上面的例子我们知道 Spring Data Elasticserch 满足不了复杂的查询。所以我们需要使用源生查询。我们可以借助Respostiroysearch查询。

image-20200427110317615.png

从图中的方法名我们知道,要进行查询需要借助一个QuerBuilder对象。这个对象需要借助QueryBuilders去产生。

QueryBuilders这个里面定义了大量的方法,不同的方法就会产生不同类型的对象

image-20200427110644151.png

接下来我们来使用一个简单的词条匹配查询

2. SearchQuery

通过上面的例子,我们发现search方法还可以传入一个SearchQuery对象,并且同时返回的是一个Page对象

image-20200427111546557.png

SearchQuery对象是通过 NativeSearchQueryBuilder得到。

image-20200427113114557.png image-20200427113050914.png

我们发现其实NativeSearchQueryBuilder就是一个条件整合器,把各个条件整合到一块。所以也推荐大家使用

NativeSearchQueryBuilder去查询条件

当然我们也可以在这里面去添加分页操作。

image-20200427113446263.png image-20200427113419825.png

当然通过Page对象我们也可以去得到一些分页信息:

image-20200427121612465.png

1.5.6 聚合

1.桶

划分桶,我们这里可以品牌(brand)去划分桶,先用kibana方式去访问一下:

image-20200427122446247.png

java代码展示:

 @Test
    public void aggs() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 不查询任何结果,相当于设置 size = 0
        nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(null,null));
        // 添加聚合
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("brand_aggs").field("brand"));
        // 查询 得到分页对象
        Page<Goods> page = goodsRepository.search(nativeSearchQueryBuilder.build());
        // 将分页对象强转成 聚合分页对象(AggregationPage)
        AggregatedPage<Goods> aggregatedPage = (AggregatedPage<Goods>) page;
        // 获取所有聚合,因为聚合里面也可以嵌套聚合
      //  System.out.println(aggregatedPage.getAggregations());
        // 根据名字获取聚合
        Aggregation aggregation = aggregatedPage.getAggregation("brand_aggs");
        //System.out.println(aggregation);
        /**
         *  获取聚合里面的桶
         *      因为我们去进行聚合的时候,是根据 String 类型  brand字段去进行terms聚合
         *      所以我们需要强转成 StringTerms对象才能获取里面的桶,当然除了该类型对象以外,当然还有其他的类型对象
          */
        StringTerms stringTerms = (StringTerms) aggregation;
        // 得到所有的桶
        List<StringTerms.Bucket> buckets = stringTerms.getBuckets();
        buckets.forEach(t -> {
            System.out.print(t.getKeyAsString() + ":"); // 得到桶里面的key
            System.out.print(t.getDocCount()); // 得到桶里面的 doc_count
            System.out.println();
        });


    }
image-20200427124744378.png

当然远程的查询也可以用ElasticSearchTemplate实现

image-20200427125059491.png image-20200427125114820.png

2.指标(度量)

先通过kibana查询

image-20200427125635907.png

桶内指标其实就是一个聚合里面嵌套另一个聚合。

java代码:

@Test
    public void metirc() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 不含任何结果,相当于设置 size = 0
        nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(null,null));
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("brand_aggs").field("brand")
                                                .subAggregation(AggregationBuilders.avg("price_avg").field("price")));

        AggregatedPage<Goods> aggregatedPage = (AggregatedPage<Goods>) goodsRepository.search(nativeSearchQueryBuilder.build());
        /**
         *  通过kibana访问我们知道:
         *      brand_aggs 聚合里面包含了
         *             price_avg 聚合
         *      因此我们先要获取 brand_aggs 再获取 price_avg聚合
         */
        StringTerms stringTerms = (StringTerms) aggregatedPage.getAggregation("brand_aggs");

        List<StringTerms.Bucket> buckets = stringTerms.getBuckets();
        buckets.forEach(t -> {
            System.out.print(t.getKeyAsString() + ":"); // 得到桶里面的key
            System.out.print(t.getDocCount()); // 得到桶里面的 doc_count
            System.out.println();

            // 桶里面还包含着指标计算的结果
            Aggregation aggregation = t.getAggregations().asMap().get("price_avg");
            // 将该聚合转换成 InternalAvg
            InternalAvg internalAvg = (InternalAvg) aggregation;
            System.out.println(internalAvg.getValue());

        });

    }
image-20200427131845820.png

自此,java 去连接 es操作完成了。

上一篇 下一篇

猜你喜欢

热点阅读