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

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值