SpringCloud源码探析(七)-整合Elasticsearch

news2024/11/25 1:48:03

1.概述

ElasticSearch是一个基于Lucene的搜索服务器,提供了一个分布式多用户能力的全文搜索引擎。它是基于JAVA语言开发,并且是基于RESTful web接口进行查询和结果返回,是一款非常流行的企业级搜索引擎。Elasticsearch的核心功能包括存储数据和快速搜索、分析数据。本文将从elasticsearch的基本属性和其使用方式进行分析,帮助大家快速理解(注意:本文演示的案例是基于单节点)。

2.Elasticsearch使用

Elasticsearch的安装可参考文章SpringCloud从入门到放弃之链路追踪二(Sleuth+Zipkin+Kafka+Logstash),正确安装并启动,访问地址:http://服务器IP:9200/,可得到如下数据(部分数据可根能会由于安装时配置和版本不一致有所区别):
在这里插入图片描述
安装成功之后,由于Elasticsearch默认不具备中文分词能力,因此需要安装中文分词器,安装方式如下。

2.1 安装中文分词器

由于Elasticsearch对中文的处理并非很擅长,因此需要安装中文分词器来帮助处理,本文基于centos 7.6安装了Elasticsearch 7.6.2 ik分词器,具体如下:
1.下载Elasticsearch中文分词器安装包
中文分词器的安装包在GitHub上,需要注意的是,分词器的版本需要和Elasticsearch的版本一致。例如我的Elasticsearch版本是7.6.2,则分词器也要下载对应的7.6.2版本。
在这里插入图片描述
将上述中文分词包解压到Elasticsearch安装路径中plugins目录下,然后重启即可,Elasticsearch会自动加载分词包。下载时要需要下载zip包,然后到服务器上进行解压,下载source code会缺少配置。zip包解压后如下图所示:
在这里插入图片描述
2.配置词库

在这里插入图片描述
在ik分词器中,可以扩展自己的词典来适应某些场景,也可以禁用某些敏感词条,只需要修改ik分词器中的config目录中的IKAnalyzer.cfg.xml文件:
在这里插入图片描述
ext.dic和stopword.dic与IKAnalyzer.cfg.xml在同一路径下,ext.dic中添加需要扩容的词语(例如:奥里给、躺平等),stopword.dic添加一些禁用词条(例如:毛片等)。

2.2 Elasticsearch中核心概念介绍

Elasticsearch关系型数据库
索引数据库(database)
类型数据库表(table)
文档一行数据(row)
字段一列数据(column)
映射数据库的组织和结构(schema)

针对上述概念的解释如下:

1.索引(index):索引是相似特征文档的集合,类似于关系型数据库中的数据库(database),一般同一个环境中索引名称具有唯一性,通常根据索引名称来对文档进行查询、添加、删除、修改等操作;
2.类型(type):一个类型通常是一个索引的一个逻辑分类或分区(类似于关系型数据库中的表),允许在一个索引下存储不同类型的文档,例如:用户类型、角色类型、菜单类型等;从6.0.0开始单个索引中只能有一个类型,7.0.0以后将将不建议使用,8.0.0 以后完全不支持;目前Type已经被Deprecated,在7.0开始,一个索引只能建一个Type为_doc;现阶段index反而更像一张表;
3.文档(document):一个文档是可以被索引的基础信息单元(类似于关系型数据库中一行数据)。文档可以存储JSON格式数据,数据可以嵌套,在一个所以在,可以存储多个文档,且文档必须被索引;
4.字段(field)
组成文档信息的最小单元(类似于关系型数据库中的列),比如用户类型中的姓名、年龄;
5.映射(mapping):用来定义文档及其所包含的字段如何被存储和索引(类似于关系型数据库中的schema)。mapping常见的属性有:type(数据类型)、index(是否索引)、analyzer(分词器)、properties(子字段)。

核心概念还包括集群、节点和分片,如下所示:

名称概念
集群ElasticSearch集群实际上是一个分布式系统,与所有分布式系统类似,它是为了保证数据和服务的高可用性,一个集群有多个节点,随着请求量和数据量的不断增长,系统可以将数据均匀分布到不同节点,实现横向扩展 。
节点每个节点就是一个ElasticSearch服务,也就是一个JAVA进程,它是组成集群的最小单位,每个节点都有一个唯一名字,默认在节点启动时生成一个uuid作为节点名(也可以指定名称),每个集群可以由任意多个节点组成,如果只启动一个节点,则就是单节点的集群。
分片分片是用来解决节点的容量上限问题,通过主分片可以将数据分布到集群内所有节点,类似于关系型数据库中的水平分表,一个分片就是一个Lucene实例,数据都会被索引到分片中,程序都是直接对index进行操作(而不是分片)。

2.2 SpringBoot整合Elasticsearch

springboot与elasticsearch对应的版本信息如下图所示:
在这里插入图片描述
本文使用的是Elasticsearch版本是7.6.2,因此对应的SpringBoot的版本为2.3.7版本。

2.2.1 引入pom

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

2.2.2 操作ElasticSearch

本文使用RestHighLevelClient来对ElasticSearch进行操作演示,具体RestHighLevelClient对象创建与销毁如下所示(演示用,生产环境不建议使用):

    //创建RestHighLevelClient对象
    @Before
    public void setUp() {
        this.restHighLevelClient = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://127.0.0.1:9200")
        ));
        log.info("创建RestHighLevelClient");
    }

    //销毁RestHighLevelClient对象
    @After
    public void clear() {
        try {
            this.restHighLevelClient.close();
            log.info("销毁RestHighLevelClient");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

1.创建index
ES中通过Restful API接口来请求操作索引库、文档,请求内容用DSL来表示。创建索引库和mapping的语法如下所示(创建一个名称为book的index):

PUT /book
{
    "mappings" : {
        "properties" : {
          "brand" : {
            "type" : "keyword"
          },
          "category" : {
            "type" : "keyword"
          },
          "id" : {
            "type" : "keyword"
          },
          "images" : {
            "type" : "keyword",
            "index" : false
          },
          "price" : {
            "type" : "double"
          },
          "title" : {
            "type" : "text",
            "analyzer" : "ik_max_word"
          },
          "address" : {
            "type" : "text",
            "analyzer" : "ik_max_word"
          },
          "publisher" : {
            "type" : "keyword"
          }
      }
    }
}

mapping是对索引库中文档的约束,常见的mapping属性包括:

(1)type:字段数据类型,常见的简单类型有字符串、数值、布尔类型。
字符串:text(可分词的文本)、keyword(精确值,例如:国家、城市、ip地址等);
数值包括byte、short、integer、long、double、float,布尔类型只有boolean;日期类型是date,对象类型为object(内部可包含属性),基本与JAVA语法中类型一致;
这部分与JAVA中变量类型一致。
(2)index:是否创建索引,默认为true;
(3)analyzer:使用哪种分词器,默认为英文分词器,中文分词器包括:ik_max_word(常用模式,将文本做最细粒度拆分);ik_smart(将文本做最粗粒度拆分);
(4)properties:该字段的子字段;

对应利用SpringBoot创建index的代码为:

   @Test
    public void testCreateBookIndex() throws Exception {
        CreateIndexRequest request = new CreateIndexRequest("book");
        request.source(Constants.BOOK_STRING, XContentType.JSON);
        restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        System.out.println(restHighLevelClient);
    }
	
//对应常量Constants中BOOK_STRING 
public class Constants {

    public static final String BOOK_STRING = "{\n" +
            "    \"mappings\": {\n" +
            "            \"properties\": {\n" +
            "                \"id\": {\n" +
            "                    \"type\": \"keyword\"\n" +
            "                },\n" +
            "                \"title\": {\n" +
            "                    \"type\": \"text\",\n" +
            "                    \"analyzer\": \"ik_max_word\"\n" +
            "                },\n" +
            "                \"category\": {\n" +
            "                    \"type\": \"keyword\"\n" +
            "                },\n" +
            "                \"brand\": {\n" +
            "                    \"type\": \"keyword\"\n" +
            "                },\n" +
            "                \"images\": {\n" +
            "                    \"type\": \"keyword\",\n" +
            "                    \"index\":  false\n" +
            "                },\n" +
            "                \"price\": {\n" +
            "                    \"type\": \"double\"\n" +
            "                }\n" +
            "            }\n" +
            "    }\n" +
            "}";
}

代码创建成功结果返回如下:
在这里插入图片描述
2.判断index是否存在
请求restful接口操作如下:

GET /索引名

查询索引为book,若存在,返回结果如下:

{
  "book" : {
    "aliases" : { },
    "mappings" : {
      "properties" : {
        "brand" : {
          "type" : "keyword"
        },
        "category" : {
          "type" : "keyword"
        },
        "id" : {
          "type" : "keyword"
        },
        "images" : {
          "type" : "keyword",
          "index" : false
        },
        "price" : {
          "type" : "double"
        },
        "title" : {
          "type" : "text",
          "analyzer" : "ik_max_word"
        }
      }
    },
    "settings" : {
      "index" : {
        "creation_date" : "1688459211404",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "inX1UEwZSGyB3fovFAzt2g",
        "version" : {
          "created" : "7060299"
        },
        "provided_name" : "book"
      }
    }
  }
}

在springboot中可用如下代码进行判断:

   @Test
    public void testExistBookIndex() throws Exception {
        GetIndexRequest request = new GetIndexRequest("book");
        boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        log.info("exists :{}", (exists ? "索引库已经存在" : "索引库不存在"));
    }

若存在索引,则运行结果如下:
在这里插入图片描述
3.删除索引
删除索引库语法如下:

DELETE /索引名称

在springboot中利用代码删除如下:

  @Test
    public void testDeleteBookIndex() throws Exception {
        DeleteIndexRequest request = new DeleteIndexRequest("book");
        restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
        log.info("索引删除成功");
    }

运行结果如下:
在这里插入图片描述
4.更新索引库
索引库和mapping一旦创建无法修改,但是可用添加新的字段,语法如下:

PUT /book/_mapping
{
   "properties": {
     "name": {
       "type": "keyword",
       "index": false       
     }
 }
}

es的操作类RestHighLevelClient不提供直接修改索引的操作方法。

5.添加文档
添加文档的语法如下:

POST /索引名称/_doc/文档id
{
  "address" : "杭州市西湖区",
  "brand" : "莫言文学",
  "category" : "世界文学",
  "id" : 2,
  "images" : "http://1288779/1.image",
  "price" : 109.0,
  "publisher" : "浙江鲁迅文学出版社",
  "title" : "《丰乳肥臀》"
}

添加成功返回结果为:
在这里插入图片描述
利用springboot添加文档操作如下:

   @Test
    public void testAddBookDocument() throws Exception {
        IndexRequest request = new IndexRequest("book").id("2");
        Book book1 = new Book();
        book1.setBrand("莫言文学出版社");
        book1.setCategory("世界文学");
        book1.setId(2L);
        book1.setImages("http://1288779/1.image");
        book1.setPrice(109.0);
        book1.setTitle("《丰乳肥臀》");
        book1.setPublisher("浙江鲁迅文学出版社");
        book1.setAddress("杭州市西湖区");
        request.source(JSON.toJSONString(book1), XContentType.JSON);
        restHighLevelClient.index(request, RequestOptions.DEFAULT);
        log.info("添加书籍书籍成功:{}", JSON.toJSONString(book1));
    }

6.根据id查询文档
根据id查询文档语法如下:

GET /索引名称/_doc/id

springboot根据id查询文档方式如下:

   @Test
    public void testSearchBookDocument() throws Exception {
        GetRequest request = new GetRequest("book", "1");
        GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
        String result = response.getSourceAsString();
        log.info("查询返回结果为:{}", result);
        if (StringUtils.isNotBlank(result)) {
            Book book = JSON.parseObject(result, Book.class);
            log.info("book:{}", book.toString());
        }
    }

查询结果如下:
在这里插入图片描述
7.修改文档
修改文档主要有两种方式:一种是全量修改(删除旧文档,添加新文档);另一种是增量修改(修改指定字段值)。全量修改的语法如下所示:

PUT /索引库名/_doc/文档id
{
  "doc"{
  "字段1" : "值1",
  "字段2" : "值2",
  //......
  }
}

增量修改的语法如下所示:

POST /索引库名/_update/文档id
{
  "doc"{
  "字段1" : "值1",
  "字段2" : "值2",
  //......
  }
}

springboot增量修改代码如下:

  @Test
    public void testUpdateBookDocument() throws Exception {
        UpdateRequest request = new UpdateRequest("book", "1");
        request.doc((jsonBuilder()
                .startObject()
                .field("brand", "山东文学出版社")
                .endObject()));
        restHighLevelClient.update(request, RequestOptions.DEFAULT);
        log.info("更新数据成功!");
    }

更新后查询返回结果如下:
在这里插入图片描述
8.根据Id删除文档
删除文档语法如下:

DELETE /索引名称/_doc/id

springboot根据id删除文档代码如下:

@Test
public void testDeleteBookDocument() throws Exception {
    DeleteRequest request = new DeleteRequest("book", "1");
    restHighLevelClient.delete(request, RequestOptions.DEFAULT);
    log.info("删除数据成功!");
    //testSearchProductDocument();
}

9.批量添加文档

 @Test
    public void testBulkBookDocument() throws Exception {
        BulkRequest bulkRequest = new BulkRequest();
        IndexRequest request = new IndexRequest("book").id("2");
        Book book1 = new Book();
        book1.setBrand("莫言文学出版社");
        book1.setCategory("中国文学");
        book1.setId(1L);
        book1.setImages("pic1,pic2");
        book1.setPrice(99.0);
        book1.setTitle("《蛙》");
        request.source(JSON.toJSONString(book1), XContentType.JSON);
        log.info("添加数据成功!");

        IndexRequest request1 = generateBook();
        log.info("添加数据成功!");


        bulkRequest.add(request);
        bulkRequest.add(request1);
		//批量添加
        restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        //testSearchProductDocument();
    }

    //CURL+ALT+M 抽取方法
    private IndexRequest generateBook() {
        IndexRequest request1 = new IndexRequest("book").id("3");
        Book book2 = new Book();
        book2.setBrand("莫言文学出版社");
        book2.setCategory("中国文学");
        book2.setId(1L);
        book2.setImages("pic1,pic2");
        book2.setPrice(99.0);
        book2.setTitle("《酒国》");
        request1.source(JSON.toJSONString(book2), XContentType.JSON);
        return request1;
    }

批量添加会使用restHighLevelClient中的bulk方法,通过将组装好的IndexRequest对象加入BulkRequest 对象中,最后调用bulk方法统一入库。

2.3 DSL查询语法

Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括:

(1)查询所有:查询出所有数据,一般测试用,例如:match_all;
(2)全文检索查询(full text):利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:match_query、multi_match_query;
(3)精确查询:根据精确词条值查找数据,一般是keyword、数值、日期、boolean等类型字段。例如:ids,range,term;
(4)地理查询(geo):根据经纬度查询。例如:geo_distance,geo_bounding_box;
(5)复合查询(compound):复合条件是将上述各种查询条件组合起来,合并查询条件。例如:bool、function_score;

2.3.1 match_all

match_all查询语法如下:

GET /index/_search 
{
  "query": {
    "match_all": {}
  }
}

springboot中的代码为:

   @Test
    public void testMatchAll() throws Exception {
        SearchRequest searchRequest = new SearchRequest("book");
        searchRequest.source().query(QueryBuilders.matchAllQuery());
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        log.info("result:{}", response);
        SearchHits hits = response.getHits();
        TotalHits totalHits = hits.getTotalHits();
        log.info("总数为total:{}", totalHits);
        SearchHit[] hitsList = hits.getHits();
        for (SearchHit documentFields : hitsList) {
            String sourceAsString = documentFields.getSourceAsString();
            log.info("查询结果:{}", sourceAsString);
        }
    }

运行结果为:
在这里插入图片描述

2.3.2 全文检索查询

全文检索查询,会对用户输入内容分词,常用于搜索框搜索。全文检索主要有:match、multi_match。match会对用户输入内容分词,然后去倒排索引库检索,语法如下:

GET /book/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"
    }
  }
}

multi_match与match查询类似,只不过允许同事查询多个字段,语法如下:

GET /book/_search
{
  "query": {
   "multi_match": {
     "query": "TEXT",
     "fields": ["FIELD1","FIELD2"]
   }
  }
}

match是根据一个字段去查询,multi_match是根据多个字段去查询(查询字段越多性能越差),在使用中应尽量多使用match。
springboot中match查询代码如下:

 @Test
    public void testMatchQuery() throws Exception {
        SearchRequest searchRequest = new SearchRequest("book");
        searchRequest.source().query(QueryBuilders.matchQuery("all", "莫言"));
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        log.info("result:{}", response);
        SearchHits hits = response.getHits();
        TotalHits totalHits = hits.getTotalHits();
        log.info("总数为total:{}", totalHits);
        SearchHit[] hitsList = hits.getHits();
        for (SearchHit documentFields : hitsList) {
            String sourceAsString = documentFields.getSourceAsString();
            log.info("查询结果:{}", sourceAsString);
        }
    }

2.3.3 精确查询

精确查询一般是查找keyword、数值、日期、boolean等类型字段,所以不会对搜索条件分词。常见的精确查询有term和range,term是根据词条精确值查询,range是根据值得范围查询。

//查询价格为109的书籍
GET /book/_search
{
  "query": {
    "term": {
      "price": {
        "value": 109
      }
    }
  }
}

springboot使用term查询代码如下:

 @Test
    public void testBooleanQuery() throws Exception {
        SearchRequest searchRequest = new SearchRequest("book");
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery("price", 109));
        //range是范围查询
        //boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gt(100));
        searchRequest.source().query(boolQueryBuilder);

        //分页
        searchRequest.source().from(0).size(10);

        //排序
        //searchRequest.source().sort("price", SortOrder.ASC);

        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        log.info("result:{}", response);
        SearchHits hits = response.getHits();
        TotalHits totalHits = hits.getTotalHits();
        log.info("总数为total:{}", totalHits);
        SearchHit[] hitsList = hits.getHits();
        for (SearchHit documentFields : hitsList) {
            String sourceAsString = documentFields.getSourceAsString();
            log.info("查询结果:{}", sourceAsString);
        }
    }

结果如下:
在这里插入图片描述
range查询语法如下:

GET /book/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 200
      }
    }
  }
}

2.3.4 地理查询

地理查询主要根据经纬度来查询,使用场景包括:微信附近的人、附近的酒店、附近的商家等。查询语法如下:

GET /book/_search
{
  "query": {
  "geo_distance": {
    "distance": "10km",
    "FIELD": "31.21,121.5"
  }
  }
}

2.3.5复合查询

1.算分函数
复合查询就是将多种条件组合,实现更复杂的搜索。例如:function score(算分函数)。
算分函数可用控制文档的相关性算分,控制文档排名。查询语法如下:

GET /book/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "brand": "莫言文学"
        }
      },//原始查询条件,搜索相关文档并进行打分
      "functions": [
        {"filter": {
          "term": {
            "id": "1"
          }
        },//过滤条件,复合条件的文档才会被重新打分
          "weight": 10  //算分函数,算分函数会得到计算结果function score,将来会与query score运算,得到新算分
        }
      ],
       "boost_mode": "multiply"  //加权模式,定义function score与query score的运算方式,multiply是两者相乘,还有sum、avg、max等
    }
  }
}

结果如下:

{
  "took" : 7,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : 3.791412,
    "hits" : [
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 3.791412,
        "_source" : {
          "brand" : "莫言文学出版社",
          "category" : "中国文学",
          "id" : 1,
          "images" : "pic1,pic2",
          "price" : 99.0,
          "score" : 0.0,
          "title" : "《蛙》"
        }
      },
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 3.791412,
        "_source" : {
          "brand" : "莫言文学出版社",
          "category" : "中国文学",
          "id" : 1,
          "images" : "pic1,pic2",
          "price" : 99.0,
          "score" : 0.0,
          "title" : "《酒国》"
        }
      },
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.47436732,
        "_source" : {
          "address" : "杭州市西湖区",
          "brand" : "莫言文学",
          "category" : "世界文学",
          "id" : 2,
          "images" : "http://1288779/1.image",
          "price" : 109.0,
          "publisher" : "浙江鲁迅文学出版社",
          "title" : "《丰乳肥臀》"
        }
      },
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "4",
        "_score" : 0.47436732,
        "_source" : {
          "address" : "上海市静安区",
          "brand" : "莫言文学",
          "category" : "中国文学",
          "id" : 3,
          "images" : "pic1,pic2",
          "price" : 109.0,
          "publisher" : "北京文学出版社",
          "title" : "《红高粱》"
        }
      }
    ]
  }
}

常见的算分函数有:

(1)weight:给一个指定常量值,作为函数结果(function score);
(2)field_value_factor:用文档中的某个字段值作为函数结果;
(3)random_score:随机生成一个值,作为函数结果; (4)script_score:自定义计算公式,公式结果作为函数结果。

2.布尔查询
布尔查询时一个或者多个查询子句的组合,子查询的组合方式有:

(1)must:必须匹配每个子查询,类似“与”;
(2)should:选择性匹配子查询,类似“或”;
(3)must_not:必须不匹配,不参与算分,类似于“非”;
(4)filter:必须匹配,不参与算分。

查询语句如下:

GET /book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
          "brand": "莫言文学"
        }}
      ],
      "should": [
        {"term": {
          "id": {
            "value": 1
          }
        }
        },
        {"term": {
          "id": {
            "value": 1
          }
        }
        }
      ], 
      "must_not": [
        {
          "range": {
          "price": {
            "lt": 100
          }
        }}
      ]
    }
  }
  
}

查询结果如下:

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.47436732,
    "hits" : [
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.47436732,
        "_source" : {
          "address" : "杭州市西湖区",
          "brand" : "莫言文学",
          "category" : "世界文学",
          "id" : 2,
          "images" : "http://1288779/1.image",
          "price" : 109.0,
          "publisher" : "浙江鲁迅文学出版社",
          "title" : "《丰乳肥臀》"
        }
      },
      {
        "_index" : "book",
        "_type" : "_doc",
        "_id" : "4",
        "_score" : 0.47436732,
        "_source" : {
          "address" : "上海市静安区",
          "brand" : "莫言文学",
          "category" : "中国文学",
          "id" : 3,
          "images" : "pic1,pic2",
          "price" : 109.0,
          "publisher" : "北京文学出版社",
          "title" : "《红高粱》"
        }
      }
    ]
  }
}

3.高亮字段
高亮字段其实就是给指定查询字段返回一个标签,前端可根据这个标签编写css样式,来达到高亮或其它目的。高亮语法如下:

GET /book/_search
{
  "query": {
    "match": {
      "brand": "莫言文学"  //查询字段
    }
  },
  //需要高亮的字段,添加前置和后置标签
  "highlight": {
    "fields": {
      "brand": {
        "pre_tags": "<em>",
        "post_tags": "</em>"
      }
    }
  }
}

springboot中代码为:

 @Test
    public void testHighlightQuery() throws Exception {
        SearchRequest searchRequest = new SearchRequest("book");
        searchRequest.source().query(QueryBuilders.matchQuery("brand", "莫言文学"));
        //高亮
        searchRequest.source().highlighter(new HighlightBuilder().field("brand").requireFieldMatch(false));
        SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        log.info("result:{}", response);
        SearchHits hits = response.getHits();
        TotalHits totalHits = hits.getTotalHits();
        log.info("总数为total:{}", totalHits);
        SearchHit[] hitsList = hits.getHits();
        for (SearchHit hit : hitsList) {
            String sourceAsString = hit.getSourceAsString();
            Book book = JSON.parseObject(sourceAsString, Book.class);
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField highlightField = highlightFields.get("brand");
            String string = highlightField.getFragments()[0].string();
            book.setBrand(string);
            log.info("查询结果:{}", string);
        }
    }

结果如下:
在这里插入图片描述

3.小结

1.ik分词器可以帮助es更好地识别中文模式;
2.elasticsearch、kibana、springboot使用时要注意版本一致性;
3.ES中通过Restful API接口来请求操作索引库、文档,请求内容用DSL来表示。

4.参考文献

1.https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html
2.https://www.bilibili.com/video/BV1LQ4y127n4
3.https://www.elastic.co/cn/downloads/past-releases/kibana-7-6-2

5.附录

1.https://gitee.com/Marinc/nacos.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/718417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis缓存问题与缓存更新机制

目录 ​编辑 一、缓存问题 1.1 缓存穿透 1.1.1 问题来源 1.1.2 解决方案 1.1.2.1 缓存空对象 1.1.2.2 使用布隆过滤器 1.2 缓存击穿 1.2.1 问题来源 1.2.2 解决方案 1.2.2.1 设置热点数据永远不过期 1.2.2.2 新增后台定时更新缓存线程&#xff08;逻辑不过期&#xff09; 1.2.…

详解Java Synchronized锁升级原理

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;JAVA多线程 &#x1f96d;本文内…

蓝桥杯专题-试题版含答案-【猴子吃桃问题】【小光棍数】【九九乘法表】【谁是最好的Coder】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

(Linux) 套接字socket基础

文章目录 前言基本原理 Codeserverclient 核心函数socketbindlistenacceptrecvsendconnectclose 多线程改进END 前言 本文将以纯C语言描述&#xff0c;编译器gcc。 C/C没有标准的网络库&#xff0c;因为都需要用到各个平台的接口才行。 本文讲解Linux下最基础的socket编程&a…

uniapp 常用提示弹框整理

一. 加载提示弹框 在执行数据查询、页面数据渲染等过程中弹出提示。以页面渲染为例&#xff1a; //前端数据请求时&#xff0c;显示加载提示弹框 uni.showLoading({title: 加载中... }); // 数据从后端接口返回后&#xff0c;提示弹框关闭 uni.hideLoading();效果如下&#x…

Sui Builder House京都站|创意大赛获奖名单公布

Sui Builder House京都站于6月30日&#xff08;周五&#xff09;圆满结束&#xff0c;这是一次Sui生态系统项目演示和展示各自产品的良好机会。构建者们向大家展示了游戏、NFT、DeFi和基础设施赛道的项目&#xff0c;同时现场演讲还介绍了Sui的最新进展以及有关AI和用户体验设计…

React环境安装配置

React环境安装配置 一、前提二、React安装 一、前提 安装本地React环境需要Node.js&#xff0c;如果具有Node环境跳过即可。如果没有安装则可参考该篇文章安装Node环境&#xff0c;点击查看 二、React安装 全局安装React 首先打开命令行&#xff0c;建议以管理员身份输入命…

20230704测试STC32G实验箱9.6(STC32G12K128)开发板的虚拟串口(C语言深入了解)

20230704测试STC32G实验箱9.6&#xff08;STC32G12K128&#xff09;开发板的虚拟串口&#xff08;C语言深入了解&#xff09; 06第五集&#xff1a;C语言运算符和进制数入门上.mp4 07第五集&#xff1a;C语言运算符和进制数入门下.mp4 2023/7/4 19:00 下次 在【冲哥】录视频的时…

024、数据库管理之数据同步工具TiCDC

TiCDC TiCDCTiCDC介绍架构与原理适用场景对已有TiDB进行扩容部署TiCDC管理工具TiCDC同步任务查询所有TiCDC同步任务查询TiCDC指定的同步任务管理TiCDC同步任务动态更新任务监控TiCDC 实验数据同步完整实操缩容当前TiCDC节点 TiCDC TiCDC介绍 TiCDC 是一款通过拉取 TiKV 变更日…

leetcode 42-接雨水

解法一&#xff1a;双指针&#xff08;暴力按列计算&#xff09; 首先&#xff0c;如果按照列来计算的话&#xff0c;宽度一定是1了&#xff0c;我们再把每一列的雨水的高度求出来就可以了。 可以看出每一列雨水的高度&#xff0c;取决于该列左侧最高的柱子和右侧最高的柱子中…

VMware虚拟机的基本操作:快照和克隆

VMware虚拟机的基本操作 一、虚拟机快照二、克隆 一、虚拟机快照 “快照”是虚拟机磁盘文件&#xff08;VMDK&#xff09;在某个点及时的副本。系统崩溃或系统异常&#xff0c;你可以通过使用恢复到快照来保持磁盘文件系统和系统存储。当升级应用和服务器及给它们打补丁的时候…

一桩关于Json序列化引发的惨案(Go系统)

文章目录 前言突然崩了排查问题关于go的json库什么是反射 解决大结构体序列化的性能问题干掉大结构体减少反射使用一些好用的第三方序列化包 自定义序列化 写在最后 前言 一个风和日丽的下午&#xff0c;线上系统突然开始报警&#xff08;系统温馨提示&#xff0c;您的服务接口…

部署Git服务器

哈喽&#xff0c;大家好&#xff0c;本次为大家演示如何部署git服务器. 首先要准备gitea和phpstudy_pro phpstudy一路nest即可&#xff0c;可以帮你安装mysql和阿帕奇。 登录127.0.0.1:3000注册gitea即可。 如何在上传公钥的时候出现500的错误&#xff0c;加入这句便可解决…

SpringBoot+Vue+Element-ui实现文件下载

目录 1.后端代码部分 2.前端代码部分 3.效果展示 1.后端代码部分 GetMapping("downloadFile")public void downloadFile(RequestParam("filename") String filename,HttpServletResponse response) throws Exception {// 告知浏览器这是一个字节流&…

WebSocket connection to “wss://xxx/xxx“ failed

用了https后&#xff0c;需要用nginx作websocket地址的转发才能使用wss&#xff0c;别直接用端口访问 location /ws/ {proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upgrade"; proxy_pass http://localhost:10001/…

JVM源码剖析之SymbolTable和StringTable

很多读者在观看JVM相关的书籍时会看到SymbolTable和StringTable&#xff0c;书中的三言二语介绍的不是很清楚&#xff0c;并且读者的水平有限&#xff0c;导致无法理解SymbolTable和StringTable。所以特意写此篇图文并茂的文章来彻底理解SymbolTable和StringTable这两张表。 版…

Java面试Day16

1.Dubbo 是什么&#xff1f;是否了解过它的架构设计&#xff1f; Dubbo官网&#xff1a;Apache Dubbo Dubbo是一个高性能、轻量级的开源Java RPC框架&#xff0c;它提供了完整的RPC协议栈&#xff0c;包括服务发布、服务引用、负载均衡、容错、服务治理和服务监控等功能&#…

构造函数使用初始化列表+模板

构造函数使用初始化列表模板 注意对应关系&#xff1a; Stack(int size) ;template<class DataType> inline Stack<DataType>::Stack(int size) : stacksize(size), top(0) {item new DataType[stacksize];if (item ! nullptr) cout << "成功初始化栈&…

再见!Fastjson!

你为何仍用Fastjson&#xff1f; 原因可以说出5678种&#xff0c;总而言之言而总之&#xff0c;你不&#xff08;敢&#xff09;切换的原因或许只有一个&#xff1a;Fastjson的静态方法调用用着省心&#xff1b;最重要的是&#xff0c;对其它JSON库&#xff08;如Jackson/Gson…

【生活分享】《哈姆雷特》· 苏州站 - 一次有趣的音乐剧体验

平时博客里大部分都是写技术相关的文章&#xff0c;感觉内容还是比较单一的&#xff0c;也想分享一下最近的一些有趣的生活~ 6月30号的时候&#xff0c;便订好了徐俊导演编排的7月2号场《哈姆雷特》音乐剧。而实际上&#xff0c;苏州场7月1号就上映了&#xff0c;感觉那天太晚……