ElasticSearch 在 Spring 项目中的实践

【主流技术】ElasticSearch 在 Spring 项目中的实践 ElasticSearch简称es,是一个开源的高扩展的分布式全文检索引擎。它可以近乎实时的存储、检索数据,其扩展性很好,ElasticSearch是企业级应用中较为常见的技术。下面和大家分享 ElasticSearch 集成在Spring Boot 项目的一些学习心得。ElasticSearch 是基于 Lucene 实现的开源、分布式、RESTful接口的全文搜索引擎。Elasticsearch 还是一个分布式文档数据库,其中每个字段均是被索引的数据且可被搜索,它能够扩展至数以百计的服务器存储以及处理P 阅读详情

前言

ElasticSearch简称es,是一个开源的高扩展的分布式全文检索引擎。

它可以近乎实时的存储、检索数据,其扩展性很好,ElasticSearch是企业级应用中较为常见的技术。

下面和大家分享 ElasticSearch 集成在Spring Boot 项目的一些学习心得。

一、ElasticSearch概述

1.1基本认识

ElasticSearch 是基于 Lucene 实现的开源、分布式、RESTful接口的全文搜索引擎。

Elasticsearch 还是一个分布式文档数据库,其中每个字段均是被索引的数据且可被搜索,它能够扩展至数以百计的服务器存储以及处理PB级的数据。

Elasticsearch 可以通过简单的 RESTful 风格 API 来隐藏 Lucene 的复杂性,让搜索变得更加简单。

1.2核心概念

Elasticsearch 的核心概念是 Elasticsearch 搜索的过程,在搜索的过程中,Elasticsearch 的存储过程、数据结构都会有所涉及。

  • 对比关系型数据库

    表1

    关系型数据库Elasticsearch
    数据库(DataBase)索引(indices)
    表(table)types(已弃用)
    行(rows)documents
    字段(columns)fields

注:

  1. Elasticsearch (集群)中可以包含多个indices(对应库),每个索引中可以包含多个types(对应表),每个types下面又包含多个documents(对应行记录),每个documents中又含有多个fields(对应字段)。
  2. Elasticsearch 中一切数据的格式都是 JSON。
  • documents

  • fields

  • types(弃用)

  • indices

    Elasticsearch 中的索引是一个非常大的文档集合,存储了映射类型的字段和其它设置,被存储在各个分片上。

1.3倒排索引

Elasticsearch 使用一种名为倒排索引的结构进行搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。

传统数据库的搜索结构一般以id为主,可以一一对应数据库中的所有内容,即key-value的形式。

而倒排索引则与之相反,以内容为主,将所有不重复的内容记录按照匹配的程度(阈值)进行展示,即value-key的形式。

以下举两个例子来进行说明。

  • 例一:

    在关系型数据库中,数据是按照id的顺序进行约定的,记录的id具有唯一性,方便人们使用id去确定内容,如表2所示:

    表2

    idlabel
    1java
    2java
    3java,python
    4python
  • 例二:

    在 ElasticSearch 中使用倒排索引:数据是按照不重复的内容进行约定的,不重复的内容具有唯一性,这样可以快速地找出符合内容的记录,再根据匹配的阈值去进行展示,如表3所示:

    labelid
    java1,2,3
    python4,3

1.4了解ELK

ELK 是 ElasticSearch、Logstash、Kibana这三大开源框架首字母大写简称。

其中 Logstash 是中央数据流引擎,用于从不同目标(文件/数据存储/MQ)中收集不同的数据格式,经过过滤后支持输送到不同的目的地(文件/MQ/Redis/elasticsearch/kafka等)。

而 Kibana 可以将 ElasticSearch 的数据通过友好的可视化界面展示出来,且提供实时分析的功能。

ELK一般来说是一个日志分析架构技术栈的总称,但实际上 ELK 不仅仅适用于日志分析,它还可以支持任何其它数据分析和收集的场景,日志的分析和收集只是更具有代表性,并非 ELK 的唯一用途。

二、ElasticSearch(插件)安装

2.1安装声明

  • 适用于JDK1.8及以上版本
  • ElasticSearch客户端
  • 界面工具
  • ElasticSearch版本与Maven依赖版本对应

2.2 ElasticSearch下载

官网地址: https://www.elastic.co

下载地址(7.6.1版本): https://www.elastic.co/downloads/past-releases/elasticsearch-7-6-1,推荐迅雷下载(速度较快)。

2.3安装ElasticSearch

将下载好的压缩包进行安装即可,解压后如下图所示:

解压安装

  • 安装目录

    • bin 启动文件

    • config 配置文件

      • log4j2:日志配置文件
      • jvm.options:Java 虚拟机相关配置
      • elasticsearch.yml:elasticsearch配置文件,默认 9200 端口,解决跨域问题。
    • lib 相关jar包

    • modules 功能模块

    • plugins 插件(如IK分词器)

2.4启动ElasticSearch

打开bin文件夹下的elasticsearch.bat文件,双击启动后访问默认地址:localhost:9200,即可得到以下json格式的数据:

{
  "name" : "ZHUZQC",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "AMdLpCANStmY8kvou9-OtQ",
  "version" : {
    "number" : "7.6.1",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "aa751e09be0a5072e8570670309b1f12348f023b",
    "build_date" : "2020-02-29T00:15:25.529771Z",
    "build_snapshot" : false,
    "lucene_version" : "8.4.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

2.5可视化界面-head安装

下载地址: https://github.com/mobz/elasticsearch-head/

安装要求:先检查计算机是否安装node.js、npm

  • 步骤一:在解压后的文件目录下进入cmd,使用 cnpm install 命令安装镜像文件;

  • 步骤二:使用 npm run start 命令启动,得到 http://localhost:9100

  • 步骤三:解决跨域问题,打开 elasticsearch.yml 文件,输入以下代码后保存:

    http.cors.enabled: true
    http.cors.allow-origin: "*"
  • 再次重启elasticsearch,进入http://localhost:9200 验证是否启动成功

  • 最后进入 http://localhost:9100 ,得到以下界面,则head启动成功:

head启动

2.6初步创建索引

可以把索引当作一个数据库来使用,具体的创建如下步骤所示:

  • 步骤一:点击Indices,在弹出的提示框中填写索引名称,点击确认;

  • 步骤二:可以在head界面中看到该索引,如下图所示:

创建索引

注:head仅可以当作一个数据可视化的展示工具,对于查询语句推荐使用Kibana。

2.7安装Kibana工具

Kibana是一个针对 ElasticSearch 的开源分析、可视化平台,用于搜索、查看交互存储在ElasticSearch中的数据。

Kibana 操作简单,基于浏览器的的用户界面可以快速创建仪表板(dashboard)并实时显示数据。

官网下载: Kibana 7.6.1 | Elastic

注意事项:Kibana 版本需要和 ElasticSearch 的版本保持一致。

安装步骤如下:

  • 步骤一:打开解压缩后的bin文件夹,双击.bat文件;
  • 步骤二:打开 http://localhost:5601 进入 Kibana 界面

2.8使用Kibana工具

在开发的过程中,可供数据测试的工具有很多,比如postman、head、Chrome浏览器等,这里推荐使用 Kibana 进行数据测试。

操作界面如下图所示:

kibana界面

三、IK分词器

3.1基本介绍

在使用中文进行搜索时,我们会对要搜索的信息进行分词:将一段中文分成一个个的词语或者句子,然后将分出的词进行搜索。

默认的中文分词是一个汉字一个词,如:“你好世界”,会被分成:“你”,“好”,“世”,“界”。但这样的分词方式显然并不全面,比如还可以分成:“你好”,“世界”。

ik分词器就解决了默认分词不全面的问题,可以将中文进行不重复的分词。

ik分词器提供了两种2算法:ik_smart(最少切分)以及ik_max_word(最细颗粒度划分)。

github下载: Release v7.6.1 · medcl/elasticsearch-analysis-ik · GitHub

  • 步骤一:将解压后的所有文件放置在 elasticsearch 下的plugins文件夹下;
  • 步骤二:重启 elasticsearch

3.2使用Kibana测试

  1. 首先测试 ik_smart(最少切分)算法的分词效果,具体如图3-1所示:

    图3-1

  2. 再测试 ik_max_word(最细颗粒度划分)算法的分词效果,具体如图3-2所示:

图3-2

3.3修改本地字典

ik分词的默认字典并不能完全涵盖所有的中文分词,当我们想自定义分词时,就需要修改ik分词器的字典配置。

  • 步骤一:打开elasticsearch-7.6.1\plugins\ik\config文件夹,增加自定义dic文件;
  • 步骤二:在同一文件夹中的IKAnalyzer.cfg.xml里,将上述步骤的dic文件写入自己的扩展字典;

具体效果如下图3-3所示:

图3-3

四、Rest风格操作

ElasticSearch 使用 Rest 风格来进行一系列操作,具体的命令如图4-1所示:

图4-1

4.1创建索引

PUT /test_1/type/1
{
  "name": "zhuzqc",
  "age": 35364
}

4.2修改索引内容

GET /test_1
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "age": {
        "type": "long"
      },
      "birthdy": {
        "type": "date"
      }
    }
  }
}

4.3更新索引内容

POST /test_1/_doc/1/_update
{
  "doc": {
  "name": "noone"  
  }
}

4.4删除索引

DELETE test_2

4.5关于documents的操作

4.5.1基本操作

documents 可以看作是数据库中的行记录;

  1. 首先先生产一些 documents 数据:
PUT zhuzqc/user/3
{
  "name": "李四",
  "age": 894,
  "desc": "影流之主",
  "tags": ["劫","刺客","中单"]
}

2.获取数据:

GET zhuzqc/user/1

3.更新数据

// POST请求对指定内容进行更新
POST zhuzqc/user/1/_update
{
  "doc": {
  "name": "342rfd",
  "age": 243234
  }
}

4.简单的条件查询

// 查询统一GET开头,_search后接?,q代表query,属性:内容
GET zhuzqc/user/_search?q=name:李

如:查询zhuzqc索引中name为李四的信息,其中李四遵循默认的分词规则
GET zhuzqc/user/_search?q=name:李四

4.5.2复杂操作

上述的一些简单查询操作在企业级应用开发中使用地较少,更多地还是使用查询实现复杂的业务。

随着业务的复杂程度增加,查询的语句也随之复杂起来,在使用复杂查询的过程中必然会涉及一些 elasticsearch 的进阶语法。

对于复杂查询的操作在下一章会详细介绍。

五、查询详解

ElasticSearch引擎首先分析需要查询的字符串,根据分词器规则对其进行分词。分词之后,才会根据查询条件进行结果返回。

5.1关键字介绍

  • query 关键字:将需要查询的 JSON 参数体进行包裹,声明这是一条查询语句。
  • bool 关键字:表明返回结果类型为布尔类型。
  • keyword 关键字:keyword代表一种分词类型,表明该字段的值不会被分词器分词。
  • must 关键字:在 must 中的内容表明都是必须执行的内容,在 must 中可以创建多条语句,多条语句需同时满足条件才能执行,作用相当于 SQL 语句中的 AND 。
  • should 关键字:在 should 关键字里的内容只要满足其中一项就可以执行,作用相当于 SQL 语句中的 OR 。
  • must_not 关键字:类似于 Java 中的 != 作用,展示查询内容之外的内容。
  • match 关键字:match 的作用是匹配查询,首先经过分词器的分词,后再执行 match 查询,默认情况下:字段内容必须完整地匹配到任意一个词条(分词后),才会有返回结果。
  • 注: 如果需要查询的词有多个,可以用空格隔开。
  • match_all 关键字:待补充。
  • match_phrase 关键字:待补充。
  • term 关键字:精确查询关键字,使用 term 时首先不会对需要查询的词条进行分词,只有精确地匹配到一模一样的内容才会返回结果。
  • terms 关键字:待补充。
  • filter 关键字:对查询的内容进行筛选过滤,常使用 gt(大于)、gte(大于等于)、lt(小于)和 lte (小于等于)来进行筛选。
GET product_cloud/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "bool": {
            "should": [
              {"match": {"product_comment":"持续交付 工程师"}}
            ]
          }
        },
        {
          "bool": {
            "should": [
              {"terms": {"label_ids": [3]}}
            ]
          }
        }
      ],
      "filter": {
        "range": {
          "label_ids": {
            "gte": 0 
          }
        }
      }
    }
  }
  • score 关键字:字段内容与词条的匹配程度,分数越高,表明匹配度越高,就越符合查询结果。

  • hits 关键字:对应 Java 代码中的 hit 对象,包含了索引和文档信息,包括查询结果总数,查询出来的_doc内容(一串 JSON),分数(score)等。

  • source:需要展示的内容字段,默认是展示索引的所有字段,也可以自定义指定需要展示的字段。

  • sort关键字:可以对字段的展示进行排序;

"_source": ["product_comment","product_name","label_ids","product_solution","company_name"],
  "sort": [
    {
      "label_ids": {
        "order": "desc"
      }
    }
  ],
  "from": 0,
  "size": 3

5.2 highlight 高亮

使用 highlight 关键字可以在搜索结果中对需要高亮的字段进行高亮(可自定义样式)展示,具体代码如下:

GET product_cloud/_search
{
  "query": {
    "term": {
      "product_comment": "世界"
    }
  },
  "highlight": {
    "pre_tags": "<p class='key' style='color:red'>", 
    "post_tags": "</p>",   
    "fields": {
      "product_comment": {}
    }
  }
}

六、Spring Boot集成ElasticSearch

在 Elasticsearch 的官方文档中有对 Elasticsearch 客户端使用的详细介绍: Installation | Elasticsearch Java API Client [8.0] | Elastic

6.1添加依赖

<properties>
        <java.version>11</java.version>
        <!-- 自定义 ElasticSearch 依赖版本与安装的版本一致 -->
        <elasticsearch.verson>7.6.1</elasticsearch.verson>
</properties>

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

6.2创建对象

定义一个客户端对象:

@Configuration
public class EsConfig {
    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient restHighLevelClient = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("127.0.0.1",9200,"http")
                )
        );
        return restHighLevelClient;
    }
}

6.3分析类中的方法(索引相关API)

  • 创建索引

    @Autowired
        private RestHighLevelClient restHighLevelClient;
    
        // 测试索引的创建
        @Test
        void testCreateIndex() throws IOException {
            //1、创建索引请求
            CreateIndexRequest request = new CreateIndexRequest("zhu_index");
            //2、执行创建请求,并获得响应
            CreateIndexResponse createIndexResponse =
                    restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            System.out.println(createIndexResponse);
        }
  • 获取索引

    // 测试获取索引
        @Test
        void testExistIndex() throws IOException {
            GetIndexRequest getIndexRequest = new GetIndexRequest("zhu_index");
            boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
            System.out.println(exists);
        }
  • 删除索引

    // 测试删除索引
        @Test
        void testDeleteIndex() throws IOException {
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest();
            AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
            System.out.println(delete);
        }

七、相关API操作

API 的操作主要是将Spring Boot项目与 Elasticsearch 的 indices 与 docs 相关联起来,这样可以做到在 Elasticsearch 中对项目数据进行一系列的操作。

7.1文档API

  • 新增数据

// 测试添加文档
    @Test
    void testAddDocument() throws IOException {
        // 创建对象
        User user = new User("zzz",3);
        // 创建请求
        IndexRequest zhu_index_request = new IndexRequest("zhu_index");
        // 规则:put /zhu_index/_doc/1
        zhu_index_request.id("1");
        zhu_index_request.timeout(TimeValue.timeValueSeconds(1));
        // 将数据放入 ElasticSearch 请求(JSON格式)
        zhu_index_request.source(JSON.toJSONString(user), XContentType.JSON);
        // 客户端发送请求
        IndexResponse indexResponse = restHighLevelClient.index(zhu_index_request,            
        RequestOptions.DEFAULT);
    }

    // 添加大批量的数据
    @Test
    void testBulkRequest() throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");
        //创建数据集合
        ArrayList<User> userList = new ArrayList<>();
        userList.add(new User("zzz2",22));
        userList.add(new User("zzz3",23));
        userList.add(new User("zzz4",24));
        userList.add(new User("zzz5",25));
        userList.add(new User("zzz6",26));
        //遍历数据:批量处理
        for (int i = 0; i < userList.size(); i++) {
            // 批量添加(或更新、或删除)
            bulkRequest.add(
                    new IndexRequest("zhu_index")
                    //.id(""+(i+1))
                    .source(JSON.toJSONString(userList.get(i)), XContentType.JSON));
        }
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }
  • 获取_doc信息

@Test
    void testGetDocument() throws IOException {
        GetRequest getRequest = new GetRequest("zhu_index","1");
        GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        // 返回_source的上下文
        getRequest.fetchSourceContext(new FetchSourceContext(true));
    }
  • 更新数据

// 更新文档信息
    @Test
    void testUpdateDocument() throws IOException {
        UpdateRequest updateRequest = new UpdateRequest("zhu_index","1");
        updateRequest.timeout("1s");

        User user = new User("ZhuZhuQC",18);
        updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, 
        RequestOptions.DEFAULT);
     
    }
  • 删除数据

    与添加数据、更新数据类似,创建 DeleteRequest 对象即可。

  • 查询数据(重点)

// 查询数据
    @Test
    void testSearch() throws IOException {
        // 创建查询对象
        SearchRequest searchRequest = new SearchRequest(EsConst.ES_INDEX);
        // 构建搜索条件(精确查询、全匹配查询)
        TermQueryBuilder termQuery = QueryBuilders.termQuery("name","zzz2");
        MatchAllQueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();

        // 执行构造器
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(termQuery);
        sourceBuilder.query(matchAllQuery);

        // 设置查询时间,3秒内
        sourceBuilder.timeout(new TimeValue(3, TimeUnit.SECONDS));

        // 设置分页
        sourceBuilder.from(0);
        sourceBuilder.size(3);

        // 最后执行搜索,并返回搜索结果
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, 
        RequestOptions.DEFAULT);
        searchResponse.getHits();

        // 打印结果
        System.out.println(JSON.toJSONString(searchResponse.getHits()));
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
            System.out.println(documentFields.getSourceAsMap());
        }
    }

八、实战分析

实战部分会模拟一个真实的 ElasticSearch 搜索过程:从创建项目开始,到使用爬虫爬取数据、编写业务,再到前后端分离交互,最后搜索结果高亮展示。

8.1创建项目

创建项目的步骤可如以下几步:

  • 步骤一:导入相关依赖

    <properties>
            <java.version>11</java.version>
            <!-- 自定义 ElasticSearch 依赖版本与安装的版本一致 -->
            <elasticsearch.version>7.6.1</elasticsearch.version>
        </properties>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.83</version>
            </dependency>
  • 步骤二:编写 properties 文件

    server.port=9090
    # 关闭 thymeleaf 缓存
    spring.thymeleaf.cache=false
    
    #mysql连接配置
    spring.datasource.username=root
    spring.datasource.password=password123
    spring.datasource.url=jdbc:mysql://localhost:3306/elasticsearch-test?useSSL=false&useUnicode=true&characterEncoding=utf-8
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    #mybatis-plus日志配置
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
    #mybatis-plus逻辑删除配置,删除为1,未删除为0
    mybatis-plus.global-config.db-config.logic-delete-value = 1
    mybatis-plus.global-config.db-config.logic-not-delete-value = 0
  • 步骤三:导入前端样式

    这个步骤可以在网盘

    地址: 百度网盘 请输入提取码

    密码: rwpq

    获取对应的 zip 包,里面包括了一些前端的静态资源和样式,直接放入 resources 文件夹中即可。

  • 步骤四:编写controller

    @Controller
    public class IndexController {
    
        @GetMapping({"/","/index"})
        public String index(){
            return "index";
        }
    }

8.2爬取数据

在真实的项目中,数据可以从数据库获得,也可以从MQ(消息队列)中获得,也可以通过爬取数据(爬虫)获得,在这里介绍一下使用爬虫获取项目所需数据的过程。

1.首先导入网页解析依赖:

<!--网页解析依赖-->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.2</version>
        </dependency>

2.编写网页解析工具类(返回爬取到的数据):

@Component
public class HtmlParseUtil {

    public static List<Content> parseJD(String keyword) throws IOException {
        // 1、获取请求:https://search.jd.com/Search?keyword=java
        String reqUrl = "https://search.jd.com/Search?keyword=" + keyword;

        // 2、解析网页,返回的document对象就是页面的 js 对象
        Document document = Jsoup.parse(new URL(reqUrl), 30000);

        // 3、js 中使用的方法获取页面信息
        Element j_goodList = document.getElementById("J_goodsList");

        // 4、获取所有的 li 元素
        Elements liElements = j_goodList.getElementsByTag("li");

        //5、返回List封装对象
        ArrayList<Content> goodsList = new ArrayList<>();

        //5、获取元素中的内容,遍历的 li 对象就是每一个 li 标签
        for (Element el : liElements) {
            String price = el.getElementsByClass("p-price").eq(0).text();
            String title = el.getElementsByClass("p-name").eq(0).text();
            String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
            // 将爬取的信息放入 List 对象中
            Content content = new Content();
            content.setTitle(title);
            content.setImg(img);
            content.setPrice(price);
            goodsList.add(content);
        }
        return goodsList;
    }
}

8.3编写业务

要编写的业务只有两部分:1、将上述获取的数据放入 ElasticSearch 的索引中;2、实现 ElasticSearch 的搜索功能;

步骤一:

1.controller层:

@Autowired
    private ContentService contentService;

    @GetMapping("/parse/{keyword}")
    public Boolean parse(@PathVariable("keyword") String keyword) throws IOException {
        return contentService.parseContent(keyword);
    }

2.service层:

@Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     *  1、将解析后的数据放入 ElasticSearch 的索引中
     * */
    public Boolean parseContent(String keyword) throws IOException {
        List<Content> contents = new HtmlParseUtil().parseJD(keyword);

        //批量插入 es
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("2m");

        for (int i = 0; i < contents.size(); i++) {
            bulkRequest.add(
                    new IndexRequest("jd_goods")
                    .source(JSON.toJSONString(contents.get(i)), XContentType.JSON)
            );
        }
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return bulk.hasFailures();
    }

步骤二:

1.controller层:

@GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
    public List<Map<String, Object>> search(@PathVariable("keyword") String keyword,
                                            @PathVariable("pageNo") Integer pageNo,
                                            @PathVariable("pageSize") Integer pageSize) throws IOException {
        return contentService.searchPage(keyword, pageNo, pageSize);
    }

2.service层:

/**
     * 2、获取数据后实现搜索功能
     * */
    public List<Map<String,Object>> searchPage(String keyword, Integer pageNo, Integer pageSize) throws IOException {
        if (pageNo <= 1){
            pageNo = 1;
        }
        //条件搜索
        SearchRequest searchRequest = new SearchRequest("jd_goods");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //精准匹配
        TermQueryBuilder titleTermQuery = QueryBuilders.termQuery("title", keyword);
        sourceBuilder.query(titleTermQuery);
        sourceBuilder.timeout(new TimeValue(3, TimeUnit.SECONDS));
        //分页
        sourceBuilder.from(pageNo);
        sourceBuilder.size(10);
        //执行搜索
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
        //解析结果
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
            list.add(documentFields.getSourceAsMap());
        }
        return list;
    }

8.4前后端交互

前后端交互主要是通过接口查询数据并返回:前端有请求参数(关键字、分页参数)后,后端通过关键字去 elasticsearch 索引中进行筛选,最终将结果返回给前端现实的一个过程。

在这里主要分析一下前端是怎么获得后端接口参数的,后端的接口在上述业务编写中已经包含了。

  • 引入Vue、axios

<!--前端使用Vue-->
<script th:src="@{/js/axios.min.js}"></script>
<script th:src="@{/js/vue.min.js}"></script>
  • 在Vue中绑定

<script>
    new Vue({
        el: '#app',
        data:{
            // 搜索关键字
            keyword: '',
            //返回结果
            results: []
        }
    })
</script>
  • 对接后端接口并返回

<script>       
       methods: {
            searchKey(){
                let keyword = this.keyword;
                console.log(keyword);
                //对接后端接口:关键字、分页参数
                axios.get('search/' + keyword + '/1/20').then(response=>{
                      console.log(response);
                      //绑定数据
                      this.results = response.data;
                })
            }
        }
</script>

8.4搜索高亮

关键字高亮总结来说,就是将原来搜索内容中的关键字置换为加了样式的关键字,进而展示出高亮效果。

主要逻辑在于,获取到 Hits 对象后,遍历关键字字段,将高亮的关键字重新放入 Hits 集合中。

具体代码如下:

//解析结果
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
            //解析高亮字段,遍历整个 Hits 对象
            Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
            //获取到关键字的字段
            HighlightField title = highlightFields.get("title");
            Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
            //置换为高亮字段:将原来的字段替换为高亮的字段
            if(title != null){
                Text[] fragments = title.fragments();
                //定义新的高亮字段
                String new_title = "";
                for (Text text : fragments) {
                    new_title += text;
                }
                //将高亮的字段放入 Map 集合
                sourceAsMap.put("title",new_title);
            }
            list.add(sourceAsMap);
        }

九、总结

ElasticSearch 作为一个分布式全文检索引擎,也可以应用在集群当中(K8S、Docker)。

ElasticSearch 实现全文检索的过程并不复杂,只要在业务需要的地方创建 ElasticSearch 索引,将数据放入索引中,就可以使用 ElasticSearch 集成在各个语言中的搜索对象进行查询操作了。

而在集成了 ElasticSearch 的 Spring Boot 项目中,无论是创建索引、精准匹配、还是字段高亮等,都是使用 ElasticSearch 对象在操作,本质上还是一个面向对象的过程。

和 Java 中的其它“对象”一样,只要灵活运用这些“对象”的使用规则和特性,就可以满足业务上的需求,对这个过程的把控也是工程师能力 的一种体现。

在 Spring Boot 项目中集成 ElasticSearch 就和大家分享到这里,如有不足,还望大家不吝赐教!

Spring Boot整合Elasticsearch,最新最全教程 要想快速入门新技术,英文文档的阅读能力必不可少。本篇文章主要参考《Spring Data Elasticsearch》Spring Boot整合Elasticsearch的官方文档。 Elasticsearch官网参考文档:https://www.elastic.co/guide/index.html Elasticsearch官方下载地址:https://www.elastic.co/cn/downloads/elasticsearch Elasticsearch目前最近版本为:7.10.1 最近系统. 阅读详情

相关推荐

Spring Boot集成Docker、Redis与Elasticsearch的实践项目

Docker是一种轻量级的虚拟化解决方案,它允许开发者打包应用程序及其依赖包到一个可移植的容器中,这些容器可以在任何支持Docker的系统上运行,保证了”一次编写,到处运行”的便利性。Docker的基本架构包括以下几个核心组件:Docker Daemon(守护进程): 长期运行在主机上,负责管理Docker对象,如镜像、容器、网络、数据卷等。Docker Client(客户端): 用户与Docker交互的命令行界面,通过发送命令来控制守护进程。Docker Images(镜像)

weixin_42598278的博客 891

Spring整合Elasticsearch

Spring整合Elasticsearch对索引库的新增和简单查询

有趣的灵魂万里挑一,好看的皮囊你又没有 2867

基于Spring Boot和MySQL的AI搜索引擎开发实战

Spring Boot是由Pivotal团队提供的全新框架,它继承了Spring生态系统的强大功能,并在此基础上提供了更多创新特性。开发者可以借助Spring Boot快速启动一个项目,而无需进行繁琐的配置。利用“约定优于配置”的原则,Spring Boot自动配置功能可以极大提高开发效率,简化项目结构。MySQL是一种关系型数据库管理系统,由瑞典MySQL AB公司开发,后由Sun公司收购,最终被甲骨文公司(Oracle Corporation)收购。

weixin_42527589的博客 657

SpringBoot:SpringBoot整合Elasticsearch案例

声明:原作者:微信公众号:悟空聊架构 文章目录声明:原作者:微信公众号:悟空聊架构前言一、Elasticsearch 组件库介绍二、整合检索服务1.1 添加搜索服务模块1.2 配置 Maven 依赖1.3 注册搜索服务到注册中心1.4 添加 ES 配置类1.5 测试 ES Client 自动加载1.6 测试 ES 简单插入数据1.7 测试 ES 查询复杂语句1.7.1 构造检索条件17.2 获取命中记录的详情1.7.3 获取年龄分布聚合信息1.7.4 获取平均薪资聚合信息三、实战:同步 ES 数据3.1 定

欢迎来到 ABin-阿斌的博客:写一生代码,创一世佳话,筑一览芳华。 2万+

mysql数据目录迁移后表不存在_mysql 直接从date 文件夹备份表,还原数据库之后提示 table doesn`t exist的原因和解决方法...

补充:正常情况下,建议数据库备份最好用工具进行备份,通过拷贝数据库表进行数据迁移,不同的环境会出现各种不同的意外问题。背景:今天在整理一个网站的时候,操作系统由于系统自动更新导致一直出现系统蓝屏死机,唉,悲剧了,于是重新安装了系统 windows server 2008 enterprise 32bit。详情:系统安装完成后,重新配置之前那个网站,appache服务配好之后,再配置数据的时候是这样...

weixin_39628271的博客 2674

LabVIEW 调用MATLAB 进行图像处理的实现

程机械及设备的变形、振动、冲击、粗糙度、刚度和强度等特性,还可用在土木结构、水利设施的变形测量[4][5]。因此,该技术在机械、土木、水利、电器、航空航天、兵器工业及生物医学等领域的检测中具有非常重要的地位。将物面前方空间的散斑用摄像设备记录下来,当物体运动或由于受力而变形时,这些随机分布的散斑图也随之在空间按一定的规律运动,因此能用记录的散斑图在计算机上分析物体的运动和变形,这就是电子散斑技术。当物体的漫反射表面被激光照射时,漫反射的光波在物体表面的前方相互干涉,由于漫反射光的。

qq_37971227的博客 3874

springboot整合elasticsearch

spring boot整合elasticsearch详细教程讲解,本文重点讲怎么利用官方文档来写出自己的方法,不用看复杂的文章,只需要搞清楚流程套路,再复杂的方法都能实现,不明白流程套路,怎么写都不顺畅。

zwjzone的博客 2万+

Cursor生成JAVA相关的关键词提示规则

你是 Java 编程、Spring Boot、Spring Framework、Maven、JUnit 及相关 Java 技术的专家。在项目根目录创建一个.curstorrules文件(注意有个小数点),之后在该文件内填入下面内容。始终遵循 SOLID 原则,保持高内聚低耦合的 Spring Boot 应用设计。Java 和 Spring Boot 使用规范。Spring Boot 具体规范。

kitonGao的博客 2118

Spring和Elasticsearch全文搜索整合详解

Spring和Elasticsearch全文搜索整合详解 一、概述 ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。 ES是ElasticSearch的缩写; ELK是三个开源软件的缩写,分别...

feiyangtianyao的专栏 3457

100天精通Python(数据分析篇)——第53天:初识pandas模块_基础介绍

一、初始pandas 1. 什么是pandas? 2. 为什么要学习pandas? 3. pandas的优势 4. 下载安装pandas 二、Pandas的数据类型 1. Series 2. DataFrame

努力让自己发光,对的人才能迎着光而来 17万+

大学计算 - NumPy库的矩阵运算 实训答案

本文介绍了三个NumPy矩阵操作的实验练习。第一关练习ndarray类型操作,包括矩阵构造、元素平方和计算、矩阵乘积运算和逆矩阵行列式计算;第二关针对matrix类型,涉及绝对值最大值获取、特定矩阵乘积运算和转置矩阵特征值计算;第三关实现Moore-Penrose伪逆矩阵计算,并与NumPy内置pinv函数结果比较误差。实验涵盖了NumPy核心矩阵操作,展示了从基础运算到高级矩阵分解的应用,帮助掌握科学计算中的矩阵处理技巧。

2403_89314916的博客 311

SpringBoot集成ES(ElasticSearch)

Java项目集成ES以及基本使用示例

愿你我都能实现自己的梦想 1962

在windows中安装并启动ActiveMQ5.16.2

打开ActiveMQ官网 下载ActiveMQ 点击apache-activemq-5.16.2-bin.zip进行下载 下载发后解压到c盘根目录下 在powershell中输入 ./activemq start启动 ActiveMQ服务 htpp://127.0.0.1:8161访问ActiveMQ控制台 ...

934

Spring AI 使用 Elasticsearch 作为向量数据库

一篇文章带你入门 Spring AI 知识库开发,选用 ES 作为 AI 向量数据库。

xyendjsj的博客 2666

MacBook Air老本重装系统

手里的这个本子型号是A1466,cpu i5,4G内存,硬盘ssd 120G。 重装系统可以在原系统上升级到更新版本,或者用U盘安装。 先确定,这个本子可以安装什么系统。 最权威的信息当然是apple的官网,地址:https://support.apple.com/zh-cn/HT211683 在“检查兼容性”中,可以依次检查每个系统兼容的硬件型号。Mojave是A1466能安装的最高版本。 确认兼容性后,下一步就是下载安装文件。还是这个页面稍下面一点可以找到。 这里告诉用户文件可以在什

bigwood99的博客 2万+

COMSOL介电弹性体数值仿真指南:预拉伸圆形DEA的稳态变形分析(II)

本期博客是《COMSOL介电弹性体数值仿真指南》系列博客中的一期。聚焦于介电弹性体驱动器(DEA)中最为经典的预拉伸圆形DEA驱动器构型,对其在COMSOL平台中的具体建模步骤进行介绍与讨论。

忘了面孔的Batou 355

[特殊字符]深入掌握 ElasticSearch:从原理到企业级最佳实践

本文系统介绍了ElasticSearch的核心原理与应用实践。主要内容包括:ES的分布式架构与分片机制;电商平台商品搜索的实战案例,涵盖数据建模、索引设计及查询优化;SpringBoot集成ES的最佳实践代码示例;以及生产环境中的集群优化策略,如分片设计、查询优化、高可用保障等。文章还总结了企业常见问题应对措施,帮助开发者从基础使用提升到企业级应用水平,充分发挥ES在全文检索、实时分析等场景的优势。

就是帅改不了 1191

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

ES在SpringBoot集成使用

按i进入插入模式,就可以编辑了,编辑完按ESC退出编辑模式,输入:,进入底行模式,再输入wq强制保存并退出,这些命令不懂的话去学下liunx基础。可以进行索引的创建、文档的增删改查等操作。Kibana是ES的一个图形化操作工具,也可以使用es-head,这里我本人使用的是es-head,因此我讲的也是es-head安装。其中的高亮显示是在ES7.9.x版本才有的好像,我在ES7.7版本显示报错,有一个方法不支持了。- http://localhost:9200 //es容器的地址,ip要换成虚拟机ip。

2509_94095062的博客 1146

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

springboot整合elasticsearch教程

最近因为项目需求,需引入elasticsearch全文搜索引擎,以达到对于文章的快速搜索,并且引入ik分词器,以此实现更智能化的搜索服务。 1.pom.xml需先添加依赖的jar包。 <dependency> <groupId>org.springframework.boot</groupId> <arti...

qq_42077317的博客 302

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Spring Boot 项目各模块整合

创建一个配置类,继承仅适用于 Spring Boot 2.x但在中,不再推荐继承该类,而是使用Lambda DSL 配置方式。步骤操作1️⃣添加依赖2️⃣(可选)创建配置类3️⃣定义用户来源(内存 / 数据库 / LDAP)4️⃣配置 URL 权限规则(公开 / 登录 / 角色)5️⃣(生产)使用加密密码6️⃣(可选)启用方法级安全@Document(indexName = "products") // 索引名(ES中相当于数据库表)@Id // ES _id 字段。

qq_34823185的博客 1414

ElasticSearch学习笔记

本文主要介绍了Elasticsearch(ES)与Solr的对比、ES的安装配置、核心概念及实际应用。主要内容包括: ES与Solr对比:ES开箱即用,Solr安装复杂;ES自带分布式协调功能,Solr依赖ZooKeeper;Solr支持更多数据格式,ES仅支持JSON;Solr查询快但更新慢,适合电商等查询多的场景,ES实时性更强。 ES安装与配置:从官网下载解压即可使用,介绍了目录结构和关键配置文件(如elasticsearch.yml)。通过插件(如elasticsearch-head)实现可视化,并

2403_87486006的博客 1657

Spring Boot 使用 Elasticsearch

使用方法:创建一个查询,匹配所有文档。示例:QueryBuilders.matchAllQuery()注意事项:这种查询不加任何条件,会返回索引中的所有文档,可能会影响性能,特别是文档数量很多时。使用方法:对指定字段执行全文搜索查询。示例:QueryBuilders.matchQuery(“fieldName”, “text to search”)注意事项:默认会对文本进行分词处理,然后进行搜索。使用方法:允许你在多个字段上执行匹配查询。

qq_41860765的博客 1637

ES 新手入门:10分钟搞定项目集成与基础使用

假设我们要存“用户”信息,比如 ID、姓名、手机号、邮箱。@Id// getter / setter 省略:告诉 ES,这个类对应 ES 里的user索引(你可以理解成“表”)。@Id:主键,ES 会用它做文档 ID。我们的经验是:字段名尽量用英文小写,别用下划线,避免映射出问题。ES 确实强大,但新手最容易犯的错就是“想一口吃成胖子”——又是调分片,又是搞高亮,又是聚合分析……结果连最基础的增删改查都没跑通。在我看来,先让它跑起来,能用就行。等真遇到性能问题或功能瓶颈,再深入优化。

记录学习路上的每一个 “啊哈!” 时刻 欢迎交流,共同进步! 924

Elasticsearch 详解 + 高级实战用法(Spring Boot + Java)

本文简明介绍Elasticsearch(ES)核心概念与SpringBoot整合实践。主要内容包括: ES核心优势:分布式搜索、近实时检索、强大全文检索和聚合分析能力 基础整合:SpringBoot配置、实体映射、Repository操作 高级应用:复杂搜索(多字段+高亮)、聚合分析、中文分词优化 生产实践:数据同步方案、常见问题解决方案和性能优化建议 典型应用场景:商品搜索、日志分析、用户行为分析等 文章通过对比传统数据库,突出ES在搜索和分析场景的优势,并提供实际代码示例,帮助开发者快速掌握ES核心功能

qq_45202803的博客 805

Springboot中使用Elasticsearch(部署+使用+讲解 最完整)

这里只做演示和介绍,如果只需要了解在Java中使用可跳过,去看第四部分,但是这些还是很有必要了解一下。Docs。

qq568018076的博客 394

Spring Boot + Elasticsearch 学习项目 —— 项目说明与踩坑实录

本文介绍了一个基于Spring Boot 2.7.18整合Elasticsearch 7.17.x的学习项目。项目通过spring-boot-starter-data-elasticsearch实现城市数据的CRUD操作,并对外提供RESTful接口。核心内容包括:1) 使用@Document注解映射实体类与ES索引;2) 继承ElasticsearchRepository实现基础操作;3) 通过FunctionScoreQueryBuilder实现加权搜索;4) 配置REST客户端连接ES 9200端口。

马优晨 434
上一篇: spring源码解析:元注解功能的实现
下一篇: SpringBoot整合SpringSecurityOauth2实现鉴权-动态权限
JavaShark
博客等级 码龄4年 184粉丝 438原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值