SpringBoot简单整合ElasticSearch实践

2025-12-06 19:50

本文主要是介绍SpringBoot简单整合ElasticSearch实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringBoot简单整合ElasticSearch实践》Elasticsearch支持结构化和非结构化数据检索,通过索引创建和倒排索引文档,提高搜索效率,它基于Lucene封装,分为索引库、类型...

一:ElasticSearch支持对结构化和非结构化的数据进行检索

我的理解是:把采集的数据(一般是从数据库中的数据如商品数据)转化为`一种有结构的数据`进行搜索,从而提高搜索效率,通常而言有结构的数据`查询是比较快`的。而这种结构在ES中被叫做 - `倒排索引文档`.

数据转换为倒排索引文档的整个过程叫`索引创建`,如下图:

SpringBoot简单整合ElasticSearch实践

原始数据经过:分词,词大小写转换,自然排序,单词合并形成倒排索引。

  • 1.分词需要使用到分词器,对于中文的内容更要使用中文分词器,比如:IK分词器
  • 2.为了方便搜索,忽略大小写敏感所以进行了词态和大小写转换
  • 3.单词进行排序,有序的数据可以提高查询速度,比如二分查找
  • 4.相同单词进行合并后单词只需要进行一次搜索即可。

ElasticSearch基于Lucene进行封装的,对于ElasticSearch来说他的数据是进行分片存储的,而一个分片就是一个Lucene的索引库,而Lucene的索引库分为索引区和数据区两部分。

结构如下:

SpringBoot简单整合ElasticSearch实践

二:ES的核心概念

Index:索引库

index被叫做索引库,类似于mysql的数据库database ; 一个index中包含一堆有相似结构的文档数据(Document),比如说建立一个Goods index 商品索引,里面可能就存放了所有的商品数据,通常一个商品数据(一行数据)在ES中被描述为一个document。

Type:类型

每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类,一个type下的document,都有相同的field,就好比一个表中的多行数据拥有相同的列 。在ES 7.x以上的版本中取消了这个逻辑分类。

Document&field

document是一个文档数据,也是ES中的最小数据单元,一个document 可以是一条商品数据,一个订单数据,通常用jsON数据结构表示,每个index下的type中,都可以去存储多个document。一个document里面有多个field,每个field就是一个数据字段。

索引库index的CRUD

索引库有点像关系型数据库的database数据库,比如:对于商品数据我们可以建立一个索引库,对于订单数据库我们也可以建立一个索引库

# 创建索引库

PUT /orders
{
	"settings":{
		"number_of_shards":5,	
		"number_of_replicas":1
	}
}
  • number_of_shards : 主分片数量
  • number_of_replicas :每个主分片有一个备分片

文档映射

ES的文档映射(mapping)机制用于进行字段类型确认,将每个字段匹配为一种确定的数据类型,也就是说我们存储到ES中的数据到底用什么类型去存储。

就如同Mysql创建表时候指定的每个column列的类型。 为了方便字段的检索,我们会指定存储在ES中的字段是否进行分词,但是有些字段类型可以分词,有些字段类型不可以分词,所以对于字段的类型需要我们自己去指定。 所以我们的ES的操作流程应该是 

  • 1.创建索引
  • 2.创建映射 
  • 3.添加数据

ES中的数据类型

  • 字符串:常用的类型有 text(分词) ;keyword(不分词) ;如果某个字段被指定为text那么这个字段的值就会被分词然后创建倒排索引。 如果是keyword也会创建倒排索引,只不过是把字段的值作为整体形成倒排索引,不会分词。
  • 数字:与Java差不多。有 long ; integer ; short ; double ; float
  • 日期:date
  • 逻辑:booleanpython
  • 对象:Object
  • 数组:array
  • 位置:geo_point;geo_shape

创建简单映射

语法:

PUT /索引名
{
  "mappings": {
    "properties": {
      "字段名": {
        "type": "类型"
      }
    }
  }
}

例如:创建订单索引库(orders),该索引库中的字段有id,title,amount,count;

id 使用long类型 ,title使用text类型(分词且索引),amount使用double ,count使用 integer类型。

PUT /orders
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "id": {
        "type": "long"
      },
      "title": {
        "type": "text"
      },
      "amount":{
        "type": "double"
      },
      "count":{
        "type": "integer"
      }
    }
  }
}

文档的CRUD

数据是以Docuement进行存储,通常一行数据就是一个Document。

添加文档

语法 `PUT index/type/id` : index是索引库 ,type是类型 ,id是文档的ID 。添加一个订单数据演示如下:

PUT orders/_doc/1
{
	"id":1,
    "title":"买了个表",
    "amount":120.00,
    "count":1
}

获取文档

语法 `GET index/type/id`

GET orders/_doc/1

修改文档

  • 全量修改:全量修改文档和添加文档的语法一样 ,只要ID已经存在,那么添加就会变成修改,当时要注意,之所以叫全量修改是如果已有的数据是 4个列,而修改的数据只给了3个列,那么最终的数据就只有3个列。
  • 局部修改:只是修改指定的列,其他列不动,语法:
POST /index/_update/id
{
    "doc":{
        "列" : 值, 
        "列": "值"
    }
}

例如:

POST orders/_update/1
{
    "doc":{
        "amount" : 150.00
    }
}

ES简单查询

# 查询所有数据
GET orders/_search

# 查询分页  size是每页条数; from是跳过的条数,和mysql的limit是一样的含义,效果如下:
GET orders/_search?size=2&from=2

# 携带查询参数可以通过 q= ,比如查询count为1的
GET orders/_search?q=count:1&size=10&from=0

# 需要带排序条件通过 sort=列:desc 指定 desc是倒排,正排是asc ,比如按在价格倒排
GET orders/_search?q=count:1&sort=amount:desc&size=10&from=0

ES条件查询

match : 标准匹配,会把搜索的关键字分词后再进行匹配,效果如同: where title = 鼠 or title = 标

GET /orders/_search
{
  "query": {
    "match": {
android      "title": "了"
    }
  },
  "from": 0,
  "size": 10,
  "_source": [
    "id",
    "title",
    "amount",
    "count"
  ],
  "sort": [
    {
      "amount": "desc"
    }
  ]
}
  • bool:代表的是组合查询,把多种查询方式组合到一起,bool下面包含了must和filter;must和filter里面都可以包含多个查询条件
  • must:bool组合了must和filter , must中的语句是DSL查询,filter中的语句是DSL过滤。must代表其中的条件是必须满足,还可以把must指定为 should 和 must_not;这个位置的语句会进行相关性计算,且按照分数排序,一般会把关键字查询放到这里。

should下面会带一个以上的条件,至少满足一个条件,这个文档就符合should

  • must_not : 文档必须不匹配条件
  • filter : 过滤,里面的查询语句不会处理相关性等,但是会对查询的结果进行缓存,性能好
  • range : 指的是范围 ;get是大于等于 ;let是小于等于
  • term :词元匹配,可以理解为精准匹配,可以用于字符串,数字等类型
  • from : 第2页应该是 (2 - 1 )* 每页条数10
GET /orders/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "买了个表"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "amount": {
              "gtphpe": 100,
              "lte": 200
            }
          }
        }
      ]
    China编程}
  },
  "from": 0,
  "size": 10,
  "sort": [
    {
      "amount": "desc"
    }
  ]
}

高亮查询

GET /orders/_search
{
    "query" : {
        "match": { "title": "鼠标" }
    },
    "highlight" : {
        "fields" : {
            "title": {
                "pre_tags": [
     China编程               "<span style='color:red'>"
                ],
                "post_tags": [
                    "</span>"
                ]
            }
        }
    }
}

三:SpringBoot操作ES

3.1 导入SpringBoot整合ES的依赖

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

3.2 application.yml配置

yml中对ES进行配置 , 如果是集群配置增加uri即可,单个配置如下:

spring:
  elasticsearch:
    rest:
      uris: http://192.168.231.128:9200

3.3 编写Document对象 ,该对象是对存储到ES中的数据的封装,同时文档映射也是通过它来实现

package com.wcl.es.doc;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.math.BigDecimal;

//标记该对象是ES的文档对象
//indexName 索引库
//type 类型
@Document(indexName = "orders",type = "_doc")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderDoc {

    //标记为文档ID,该ID的值会作为document的id值
    @Id
    private Long id;
    /**
     * 标题需要分词,指定为text;并使用IK分词器
     * 一般需要作为关键字搜索的字段都要指定为text,因为需要分词且创建索引
     */
    @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
    //@Field(type = FieldType.Keyword)
    private String title;

    /**
     * 指定为integer类型
     */
    @Field(type = FieldType.Integer)
    private int count;

    /**
     * 状态指定为 integer类型
     */
    @Field(type = FieldType.Integer)
    private int status;

    /**
     * 金额
     */
    @Field(type = FieldType.Double)
    private BigDecimal amount;

}

3.4 SpringBootData提供了ElasticsearchRepository 来操作ES,该接口中包含了针对ES的CRUD方法,我们编写接口集成它即可使用

@Repository
public interface OrderRepository extends ElasticsearchRepository<OrderDoc,Long> {
}

3.5 新建测试service类,注入

    @Autowired
    private OrdersRepository ordersRepository;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

3.6 测试

创建索引库

    public void createIndex() {
        // 创建索引库
        elasticsearchTemplate.createIndex(OrderDoc.class);
        // 创建映射
        elasticsearchTemplate.putMapping(OrderDoc.class);
    }

查询数据

    public void findById() {
        Optional<OrderDoc> byId = ordersRepository.findById(1L);
        System.out.println(byId.get());
    }

删除数据

    public void deleteById() {
        ordersRepository.deleteById(1L);
    }

新增(修改)数据

    public void addData() {
        ordersRepository.save(
                new OrderDoc(
                        3L,
                        "测试数据1",
                        1,
                        1,
                        new BigDecimal(1)
                )
        );
    }

条件查询

组合查询:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "买了个表"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "amount": {
              "gte": 100,
              "lte": 200
            }
          },
		  "term":{
			 "status":1
		  }
        }
      ]
    }
  },
  "from": 0,
  "size": 10,
  "sort": [
    {
      "amount": "desc"
    }
  ]
}

该语句在java中的写法:

     public void search(){
        // 查询构建器
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        // 设置分页 0 页数  10 每页显示多少条
        builder.withPageable(PageRequest.of(0, 10));

        // 设置排序(根据自己设置的字段排序)
        builder.withSort(SortBuilders.fieldSort("amount").order(SortOrder.DESC));

        // 构建组合查询 bool
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // must:{match:{title:"鼠标"}}
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", "鼠标"))
                // filter:{range:{amount:{gte:100,lte:200}}}
                .filter(QueryBuilders.rangeQuery("amount").gte(100).lte(200))
                // filter:{term:{status:1}}
                .filter(QueryBuilders.termQuery("status", 1));

        // 添加查询条件
        builder.withQuery(boolQueryBuilder);

        // 执行查询
        Page<OrderDoc> page = ordersRepository.search(builder.build());
        // 获取条数
        long total = page.getTotalElements();
        System.out.println("总条数:" + total);
        // 获取列表
        page.getContent().forEach(System.out::println);

    }

高亮查询

{
    "query" : {
        "match": { "title": "鼠标" }
    },
    "highlight" : {
        "fields" : {
            "title": {
                "pre_tags": [
                    "<span style='color:red'>"
                ],
                "post_tags": [
                    "</span>"
                ]
            }
        }
    }
}

该语句在java中的写法

    public void highlight(){
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withQuery(QueryBuilders.matchQuery("title", "鼠标"));
        // 设置高亮
        // 高亮的字段 fields.title
        HighlightBuilder.Field highlightField = new HighlightBuilder.Field("title")
                .preTags("<span style='color:red'>")
                .postTags("</span>");
        // 设置高亮
        builder.withHighlightFields(highlightField);


        Page<OrderDoc> orderDocs = elasticsearchTemplate.queryForPage(
                builder.build(),
                OrderDoc.class,
                new HighlightResultMapper()
        );

        // 获取条数
        long total = orderDocs.getTotalElements();
        System.out.println("总条数:" + total);
        // 获取列表
        orderDocs.getContent().forEach(System.out::println);
    }

聚合查询(查询最大,最小,平均,和等数据)

{
  "aggs":{ 					
    "statsAmount":{ 		
      "stats":{ 				
        "field":"amount" 	
      }
    }
  }
}

该语句在java中的写法:

     public void aggregation(){
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        
        builder.addAggregation(AggregationBuilders.max("maxAmount").field("amount"));
        // 聚合查询
        AggregatedPage<OrderDoc> orderDocs = elasticsearchTemplate.queryForPage(
                builder.build(),
                OrderDoc.class
        );

        // 获取条数
        long total = orderDocs.getTotalElements();
        System.out.println("总条数:" + total);
        // 获取列表
        orderDocs.getContent().forEach(System.out::println);

        // 获取聚合结果
        Map<String, Aggregation> asMap = orderDocs.getAggregations().getAsMap();
        asMap.entrySet().forEach(aggregationEntry -> {
            //聚合名字
            String aggName = aggregationEntry.getKey();
            Aggregation aggregation = aggregationEntry.getValue();
            System.out.println("聚合名字 = "+aggName);
            if(aggregation instanceof ParsedLongTerms){
                //对应terms聚合
                ParsedLongTerms agg = (ParsedLongTerms) aggregation;
                agg.getBuckets().forEach(bucket->{
                    String key = bucket.getKeyAsString();
                    long docCount = bucket.getDocCount();
                    System.out.println("key = "+key +" ; docCount = "+docCount);
                });
            }
            if(aggregation instanceof ParsedStats){
                //对应stats聚合
                ParsedStats agg = (ParsedStats) aggregation;
                System.out.println(agg.getAvg());
                System.out.println(agg.getMax());
                System.out.println(agg.getCount());
                System.out.println(agg.getSum());
                System.out.println(agg.getMin());
            }
            if(aggregation instanceof ParsedSum){
                //对应sum聚合
                ParsedSum agg = (ParsedSum) aggregation;
                System.out.println(agg.getValue());
            }
        });
    }

总结

以上就是springboot整合es的一些简单操作。

这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持编程China编程(www.chinasem.cn)。

这篇关于SpringBoot简单整合ElasticSearch实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156371

相关文章

Python数据验证神器Pydantic库的使用和实践中的避坑指南

《Python数据验证神器Pydantic库的使用和实践中的避坑指南》Pydantic是一个用于数据验证和设置的库,可以显著简化API接口开发,文章通过一个实际案例,展示了Pydantic如何在生产环... 目录1️⃣ 崩溃时刻:当你的API接口又双叒崩了!2️⃣ 神兵天降:3行代码解决验证难题3️⃣ 深度

C++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

Java方法重载与重写之同名方法的双面魔法(最新整理)

《Java方法重载与重写之同名方法的双面魔法(最新整理)》文章介绍了Java中的方法重载Overloading和方法重写Overriding的区别联系,方法重载是指在同一个类中,允许存在多个方法名相同... 目录Java方法重载与重写:同名方法的双面魔法方法重载(Overloading):同门师兄弟的不同绝

Spring配置扩展之JavaConfig的使用小结

《Spring配置扩展之JavaConfig的使用小结》JavaConfig是Spring框架中基于纯Java代码的配置方式,用于替代传统的XML配置,通过注解(如@Bean)定义Spring容器的组... 目录JavaConfig 的概念什么是JavaConfig?为什么使用 JavaConfig?Jav

Java数组动态扩容的实现示例

《Java数组动态扩容的实现示例》本文主要介绍了Java数组动态扩容的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1 问题2 方法3 结语1 问题实现动态的给数组添加元素效果,实现对数组扩容,原始数组使用静态分配

Java中ArrayList与顺序表示例详解

《Java中ArrayList与顺序表示例详解》顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构,:本文主要介绍Java中ArrayList与... 目录前言一、Java集合框架核心接口与分类ArrayList二、顺序表数据结构中的顺序表三、常用代码手动

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

JAVA线程的周期及调度机制详解

《JAVA线程的周期及调度机制详解》Java线程的生命周期包括NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED,线程调度依赖操作系统,采用抢占... 目录Java线程的生命周期线程状态转换示例代码JAVA线程调度机制优先级设置示例注意事项JAVA线程

JavaWeb项目创建、部署、连接数据库保姆级教程(tomcat)

《JavaWeb项目创建、部署、连接数据库保姆级教程(tomcat)》:本文主要介绍如何在IntelliJIDEA2020.1中创建和部署一个JavaWeb项目,包括创建项目、配置Tomcat服务... 目录简介:一、创建项目二、tomcat部署1、将tomcat解压在一个自己找得到路径2、在idea中添加