欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot 集成 Elasticsearch

程序员文章站 2023-02-02 11:22:23
前面在 ubuntu 完成安装 elasticsearch,现在我们SpringBoot将集成elasticsearch。 1、创建SpringBoot项目 我们这边直接引入NoSql中Spring Data Elasticsearch启动器。 创建项目完成后。 项目结构: pom文件:(新增 lo ......

前面在 ubuntu 完成安装 elasticsearch,现在我们springboot将集成elasticsearch。

1、创建springboot项目

我们这边直接引入nosql中spring data elasticsearch启动器。

SpringBoot 集成 Elasticsearch

创建项目完成后。

项目结构:

SpringBoot 集成 Elasticsearch

 

pom文件:(新增 lombok 简化pojo)

<?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 http://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.6.release</version>
        <relativepath/> <!-- lookup parent from repository -->
    </parent>
    <groupid>com.yatces.elasticsearch</groupid>
    <artifactid>elasticsearch-demo</artifactid>
    <version>0.0.1-snapshot</version>
    <name>elasticsearch-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.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <version>1.16.20</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
            </plugin>
        </plugins>
    </build>

</project>

2、添加 elasticsearch 配置

本人习惯 yml 文件,将 application.properties 重命名为 application.yml

spring:
  data:
    elasticsearch:
      cluster-name: elasticsearch
      cluster-nodes: 192.168.78.130:9300

3、新增实体类

@data
@noargsconstructor
@allargsconstructor
@document(indexname = "product",type = "item",shards = 1,replicas = 0)
public class item {

    @id
    long id;
    @field(type = fieldtype.text,analyzer = "ik_max_word")
    string title; //标题
    @field(type = fieldtype.keyword)
    string category;// 分类
    @field(type = fieldtype.keyword)
    string brand; // 品牌
    @field(type = fieldtype.double)
    double price; // 价格
    @field(index = false, type = fieldtype.keyword)
    string images; // 图片地址
}

主要注解:

@document 作用在类,标记实体类为文档对象,一般有四个属性

  indexname:对应索引库名称

  type:对应在索引库中的类型

  shards:分片数量,默认5

  replicas:副本数量,默认1

@id 作用在成员变量,标记一个字段作为id主键

@field 作用在成员变量,标记为文档的字段,并指定字段映射属性:

  type:字段类型,取值是枚举:fieldtype

  index:是否索引,布尔类型,默认是true

  store:是否存储,布尔类型,默认是false

  analyzer:分词器名称:ik_max_word

4、编写测试

4.1新建itemtest 

用于测试 elasticsearch 的使用,使用 elasticsearchtemplate 操作索引。

@runwith(springrunner.class)
@springboottest(classes = elasticsearchdemoapplication.class)
public class itemtest {
    @autowired
    private elasticsearchtemplate elasticsearchtemplate;
}

4.2创建索引和映射

@test
public void testcreate(){
    // 创建索引,会根据item类的@document注解信息来创建
    elasticsearchtemplate.createindex(item.class);
    // 配置映射,会根据item类中的id、field等字段来自动完成映射
    elasticsearchtemplate.putmapping(item.class);
}

在 kibana通过 get product/_mapping 查询结果

{
  "product": {
    "mappings": {
      "item": {
        "properties": {
          "brand": {
            "type": "keyword"
          },
          "category": {
            "type": "keyword"
          },
          "images": {
            "type": "keyword",
            "index": false
          },
          "price": {
            "type": "double"
          },
          "title": {
            "type": "text",
            "analyzer": "ik_max_word"
          }
        }
      }
    }
  }
}

4.3删除索引


@test
public void testdelete(){
//elasticsearchtemplate.deleteindex(item.class);
// indexname = "product"
elasticsearchtemplate.deleteindex("product");
}

kibana 再次查询,报404。

{
  "error": {
    "root_cause": [
      {
        "type": "index_not_found_exception",
        "reason": "no such index",
        "resource.type": "index_or_alias",
        "resource.id": "product",
        "index_uuid": "_na_",
        "index": "product"
      }
    ],
    "type": "index_not_found_exception",
    "reason": "no such index",
    "resource.type": "index_or_alias",
    "resource.id": "product",
    "index_uuid": "_na_",
    "index": "product"
  },
  "status": 404
}

4.4新建 itemrepository 

用于对 document 的操作测试

public interface itemrepository extends elasticsearchrepository<item, long>{
}

在 itemtest 中注入 itemrepository 

@autowired
private itemrepository itemrepository;

4.5新增文档

修改和新增是同一个接口,区分的依据就是id,新增用post 请求,修改用put请求。

@test
public void testsavedocument(){
    item item = new item(1l, "小米手机7", " 手机", "小米", 3499.00, "13123.jpg");
    itemrepository.save(item);
}

kibana 通过get product/_search 查询

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "product",
        "_type": "item",
        "_id": "1",
        "_score": 1,
        "_source": {
          "id": 1,
          "title": "小米手机7",
          "category": " 手机",
          "brand": "小米",
          "price": 3499,
          "images": "13123.jpg"
        }
      }
    ]
  }
}

4.6批量新增

@test
public void testsavedocumentlist() {
    list<item> list = new arraylist<>();
    list.add(new item(1l, "小米手机7", "手机", "小米", 3299.00, "13123.jpg"));
    list.add(new item(2l, "坚果手机r1", "手机", "锤子", 3699.00, "13123.jpg"));
    list.add(new item(3l, "华为meta10", "手机", "华为", 4499.00, "13123.jpg"));
    list.add(new item(4l, "小米mix2s", "手机", "小米", 4299.00, "13123.jpg"));
    list.add(new item(5l, "荣耀v10", "手机", "华为", 2799.00, "13123.jpg"));
    // 接收对象集合,实现批量新增
    itemrepository.saveall(list);
}

kibana 通过get product/_search 再次查询,得到5doc

4.7基本查询

在 elasticsearchrepository 继承下来的查询方法

SpringBoot 集成 Elasticsearch

4.7.1根据id查询

@test
public void testfindbyid(){
    optional<item> optional = itemrepository.findbyid(1l);
    system.out.println(optional.get());
}

结果

SpringBoot 集成 Elasticsearch

4.7.2查询所有

@test
public void testfindall(){
    // 查询所有,并根据 price 降序排序
    iterable<item> items = itemrepository.findall(sort.by(sort.direction.desc,"price"));
    items.foreach(system.out::println);
}

结果

SpringBoot 集成 Elasticsearch

4.8自定义方法

spring data 的提供一个强大功能,是根据方法名称自动实现功能,下述自定义规范:

SpringBoot 集成 Elasticsearch

SpringBoot 集成 Elasticsearch

itemrepository定义一个方法findbypricebetween,不用写这个方法的实现例如:根据价格区间查询所有 item

/**
 * 根据价格区间查询
 * @param price1
 * @param price2
 * @return
 */

list<item> findbypricebetween(double price1, double price2);

在 itemtest 编写测试

@test
public void testfindbypricebetween(){
    list<item> list = this.itemrepository.findbypricebetween(4000.00, 5000.00);
    list.foreach(system.out::println);
}

结果

SpringBoot 集成 Elasticsearch

4.9高级查询

4.9.1基本查询

repository 的 search 方法使用 querybuilders 构建查询条件

SpringBoot 集成 Elasticsearch

querybuilders 提供了大量的静态方法,用于生成各种不同类型的查询对象,例如:词条、模糊、通配符等querybuilder对象

SpringBoot 集成 Elasticsearch

public void testquery(){
    // 词条查询
    matchquerybuilder querybuilder = querybuilders.matchquery("title", "小米");
    // 执行查询
    iterable<item> items = this.itemrepository.search(querybuilder);
    items.foreach(system.out::println);
}

结果

SpringBoot 集成 Elasticsearch

4.9.2自定义查询

@test
public void testnativequery(){
    // 构建查询条件
    nativesearchquerybuilder querybuilder = new nativesearchquerybuilder();
    // 添加基本的分词查询
    querybuilder.withquery(querybuilders.matchquery("title", "小米"));
    // 执行搜索,获取结果
    page<item> items = this.itemrepository.search(querybuilder.build());
    // 打印总条数
    system.out.println(items.gettotalelements());
    // 打印总页数
    system.out.println(items.gettotalpages());
    items.foreach(system.out::println);
}

结果

SpringBoot 集成 Elasticsearch

nativesearchquerybuilder:spring提供的一个查询条件构建器,帮助构建json格式的请求体

page<item>:默认是分页查询,因此返回的是一个分页的结果对象,包含属性:

  totalelements:总条数

  totalpages:总页数

  iterator:迭代器,本身实现了iterator接口,因此可直接迭代得到当前页的数据

4.9.3分页查询

利用nativesearchquerybuilder可以方便的实现分页

@test
public void testnativepagequery(){
    // 构建查询条件
    nativesearchquerybuilder querybuilder = new nativesearchquerybuilder();
    // 添加基本的分词查询
    querybuilder.withquery(querybuilders.termquery("category", "手机"));

    // 初始化分页参数
    int page = 0;
    int size = 3;
    // 设置分页参数
    querybuilder.withpageable(pagerequest.of(page, size));

    // 执行搜索,获取结果
    page<item> items = this.itemrepository.search(querybuilder.build());
    // 打印总条数
    system.out.println("总条数:"+items.gettotalelements());
    // 打印总页数
    system.out.println("总页数:"+items.gettotalpages());
    // 每页大小
    system.out.println("每页大小:"+items.getsize());
    // 当前页
    system.out.println("当前页:"+items.getnumber());
    items.foreach(system.out::println);
}

结果:分页是从第0页开始

SpringBoot 集成 Elasticsearch

4.9.4排序

排序也通用通过nativesearchquerybuilder完成

@test
public void testsortquery(){
    // 构建查询条件
    nativesearchquerybuilder querybuilder = new nativesearchquerybuilder();
    // 添加基本的分词查询
    querybuilder.withquery(querybuilders.termquery("category", "手机"));

    // 排序
    querybuilder.withsort(sortbuilders.fieldsort("price").order(sortorder.desc));

    // 执行搜索,获取结果
    page<item> items = this.itemrepository.search(querybuilder.build());
    // 打印总条数
    system.out.println("总条数:"+items.gettotalelements());
    items.foreach(system.out::println);
}

 结果

SpringBoot 集成 Elasticsearch

4.10聚合

4.10.1普通聚合

按照品牌brand进行分组

@test
public void testbrandagg(){
    nativesearchquerybuilder querybuilder = new nativesearchquerybuilder();
    // 不查询任何结果
    querybuilder.withsourcefilter(new fetchsourcefilter(new string[]{""}, null));
    // 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
    querybuilder.addaggregation(
            aggregationbuilders.terms("brands").field("brand"));
    // 2、查询,需要把结果强转为aggregatedpage类型
    aggregatedpage<item> aggpage = (aggregatedpage<item>) this.itemrepository.search(querybuilder.build());
    // 3、解析
    // 3.1、从结果中取出名为brands的那个聚合,
    // 因为是利用string类型字段来进行的term聚合,所以结果要强转为stringterm类型
    stringterms agg = (stringterms) aggpage.getaggregation("brands");
    // 3.2、获取桶
    list<stringterms.bucket> buckets = agg.getbuckets();
    // 3.3、遍历
    for (stringterms.bucket bucket : buckets) {
        // 3.4、获取桶中的key,即品牌名称 和 文档数量
        system.out.println(bucket.getkeyasstring() + ":" +bucket.getdoccount());
    }
}

结果

 SpringBoot 集成 Elasticsearch

aggregationbuilders.terms("brands").field("brand") 聚合的构建工厂类aggregationbuilders所有聚合都由这个类来构建

 SpringBoot 集成 Elasticsearch

aggpage.getaggregation("brands")返回的结果都是aggregation类型对象,不过根据字段类型不同,又有不同的子类表示

 SpringBoot 集成 Elasticsearch

4.10.2嵌套聚合

@test
public void testsubavgagg(){
    nativesearchquerybuilder querybuilder = new nativesearchquerybuilder();
    // 不查询任何结果
    querybuilder.withsourcefilter(new fetchsourcefilter(new string[]{""}, null));
    // 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
    querybuilder.addaggregation(
            aggregationbuilders.terms("brands").field("brand")
                    .subaggregation(aggregationbuilders.avg("avgprice").field("price")) // 在品牌聚合桶内进行嵌套聚合,求平均值
    );
    // 2、查询,需要把结果强转为aggregatedpage类型
    aggregatedpage<item> aggpage = (aggregatedpage<item>) this.itemrepository.search(querybuilder.build());
    // 3、解析
    // 3.1、从结果中取出名为brands的那个聚合,
    // 因为是利用string类型字段来进行的term聚合,所以结果要强转为stringterm类型
    stringterms agg = (stringterms) aggpage.getaggregation("brands");
    // 3.2、获取桶
    list<stringterms.bucket> buckets = agg.getbuckets();
    // 3.3、遍历
    buckets.foreach(bucket -> {
        // 3.4、获取桶中的key,即品牌名称 ; 获取桶中的文档数量 ;获取平均值结果:
        internalavg avg = (internalavg) bucket.getaggregations().asmap().get("avgprice");
        system.out.println(bucket.getkeyasstring() + "共" + bucket.getdoccount() +",平均售价:"+ avg.getvalue() );
    });
}

结果

SpringBoot 集成 Elasticsearch