SpringCloud source code analysis (7)-Integrating Elasticsearch

1 Overview

ElasticSearch is a Lucene-based search server that provides a full-text search engine with distributed multi-user capabilities. It is developed based on JAVA language and is based on RESTful web interface for query and result return. It is a very popular enterprise-level search engine. The core functions of Elasticsearch include storing data and quickly searching and analyzing data. This article will analyze the basic properties of elasticsearch and its usage to help everyone understand quickly (note: the case demonstrated in this article is based on a single node).

2.Elasticsearch usage

For the installation of Elasticsearch, please refer to the articleSpringCloud from getting started to giving up on link tracking 2 (Sleuth+Zipkin+Kafka+Logstash), correct Install and start, access address: http://server IP:9200/, you can get the following data (some data may be different due to inconsistent configuration and version during installation):
Insert image description here
Installation After success, since Elasticsearch does not have Chinese word segmentation capabilities by default, a Chinese word segmenter needs to be installed. The installation method is as follows.

2.1 Install Chinese word segmenter

Since Elasticsearch is not very good at processing Chinese, it is necessary to install a Chinese word segmenter to help with processing. This article installs the Elasticsearch 7.6.2 ik word segmenter based on centos 7.6, as follows:
1. Download the Elasticsearch Chinese word segmenter installation package
The Chinese word segmenter installation package is on GitHub. Things to note Yes, the version of the tokenizer needs to be consistent with the version of Elasticsearch. For example, if my Elasticsearch version is 7.6.2, the word segmenter must also download the corresponding 7.6.2 version.
Insert image description here
Extract the above Chinese word segmentation package to the plugins directory in the Elasticsearch installation path, and then restart. Elasticsearch will automatically load the word segmentation package. When downloading, you need to download the zip package and then decompress it on the server. Downloading the source code will lack configuration. The zip package is decompressed as shown below:
Insert image description here
2. Configure the vocabulary

In the ik word segmenter, you can expand your own dictionary to adapt to certain scenarios, or disable certain sensitive entries. You only need to modify the IKAnalyzer.cfg.xml file in the config directory in the ik word segmenter:
Insert image description here
ext.dic and stopword.dic are in the same path as IKAnalyzer.cfg.xml. Add words that need to be expanded in ext.dic (for example: Oligui, lie equal), stopword.dic Add some prohibited terms (for example: pornographic films, etc.).

2.2 Introduction to core concepts in Elasticsearch

Elasticsearch Relational Database
index database
type Database table
document A row of data (row)
Field A column of data (column)
mapping Database organization and structure (schema)

The above concepts are explained as follows:

1. Index (index): An index is a collection of documents with similar characteristics, similar to a database in a relational database. Generally, the index name is unique in the same environment. properties, usually query, add, delete, modify, etc. documents based on the index name;
2. Type (type): A type is usually a A logical classification or partition of an index (similar to a table in a relational database), allowing different types of documents to be stored under one index, such as user types, role types, menu types, etc.; starting from 6.0.0, only There can be a type. It will be deprecated after 7.0.0 and not supported at all after 8.0.0. Currently Type has been Deprecated. Starting from 7.0, an index can only build one Type of _doc. At this stage, the index is more like A table;
3. Document (document) : A document is a basic unit of information that can be indexed (similar to a row of data in a relational database). Documents can store JSON format data, and data can be nested. Multiple documents can be stored in one document, and the documents must be indexed;
4. Field (field):
The smallest unit that makes up document information (similar to columns in a relational database), such as name and age in user types;
5. Mapping: used to define how documents and the fields they contain are stored and indexed (similar to schema in relational databases). Common attributes of mapping include: type (data type), index (whether to index), analyzer (word segmenter), and properties (subfields).

Core concepts also include clusters, nodes, and shards, as follows:

name concept
cluster The ElasticSearch cluster is actually a distributed system. Similar to all distributed systems, it is to ensure the high availability of data and services. A cluster has multiple nodes. As the request volume and data volume continue to grow, the system can Evenly distributed to different nodes to achieve horizontal expansion.
node Each node is an ElasticSearch service, which is a JAVA process. It is the smallest unit that forms a cluster. Each node has a unique name. By default, a uuid is generated as the node name when the node is started (the name can also be specified). A cluster can be composed of any number of nodes. If only one node is started, it is a single-node cluster.
Fragmentation Sharding is used to solve the problem of node capacity limit. Data can be distributed to all nodes in the cluster through primary sharding, similar to horizontal sharding in relational databases. A shard is a Lucene instance, and the data will be indexed. In sharding, the program directly operates the index (rather than sharding).

2.2 SpringBoot integrates Elasticsearch

The corresponding version information between springboot and elasticsearch is shown in the figure below:
Insert image description here
The Elasticsearch version used in this article is 7.6.2, so the corresponding SpringBoot version is version 2.3.7.

2.2.1 Introduction of 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 Operating ElasticSearch

This article uses RestHighLevelClient to demonstrate the operation of ElasticSearch. The specific creation and destruction of RestHighLevelClient objects are as follows (for demonstration purposes, not recommended for use in production environments):

    //创建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. Create index
In ES, the index library and documents are requested to be operated through the Restful API interface, and the request content is represented by DSL. The syntax for creating an index library and mapping is as follows (create an index named book):

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 is a constraint on documents in the index database. Common mapping attributes include:

(1) type: field data type. Common simple types include string, numerical, and Boolean types.
String: text (word-separable text), keyword (exact value, such as country, city, IP address, etc.);
Values ​​include byte, short , integer, long, double, float, the Boolean type is only boolean; the date type is date, and the object type is object (it can contain attributes internally), which is basically the same as the type in JAVA syntax;
This part is the same as Variable types in JAVA are consistent.
(2) index: whether to create an index, the default is true;
(3) analyzer: which word segmenter to use, the default is English word segmentation, Chinese word segmentation The tools include: ik_max_word (common mode, split the text into the finest granularity); ik_smart (split the text into the coarsest granularity);
(4) properties: subfields of this field;

The code corresponding to using SpringBoot to create an index is:

   @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" +
            "}";
}

The result of successful code creation is returned as follows:
Insert image description here
2. Determine whether the index exists
The operation of requesting the restful interface is as follows:

GET /index name

The query index is book. If it exists, the returned results are as follows:

{
    
    
  "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"
      }
    }
  }
}

The following code can be used to judge in springboot:

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

If the index exists, the running result is as follows:
Insert image description here
3. Delete the index
The syntax for deleting the index library is as follows:

DELETE /index name

Use the code in springboot to delete as follows:

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

The running results are as follows:
Insert image description here
4. Update the index library
Once created, the index library and mapping cannot be modified, but new fields can be added. The syntax is as follows :

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

The operation class RestHighLevelClient of es does not provide an operation method for directly modifying the index.

5. Add documents
The syntax for adding documents is as follows:

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

The result returned after successful addition is:
Insert image description here
The operation of adding documents using springboot is as follows:

   @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. Query documents based on id
The syntax for querying documents based on id is as follows:

GET /index_name/_doc/id

The way springboot queries documents based on ID is as follows:

   @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());
        }
    }

The query results are as follows:
Insert image description here
7. Modify the document
There are two main ways to modify the document: one is to modify the entire amount (delete the old document, add New document); the other is incremental modification (modify the specified field value). The syntax for full modification is as follows:

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

The syntax for incremental modification is as follows:

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

The springboot incremental modification code is as follows:

  @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("更新数据成功!");
    }

After the update, the query returns the following results:
Insert image description here
8. Delete documents based on Id
The syntax for deleting documents is as follows:

DELETE /index name/_doc/id

The springboot deletion document code based on id is as follows:

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

9. Add documents in batches

 @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;
    }

Batch addition will use the bulk method in restHighLevelClient, by adding the assembled IndexRequest object to the BulkRequest object, and finally calling the bulk method to uniformly store them in the database.

2.3 DSL query syntax

Elasticsearch provides a JSON-based DSL (Domain Specific Language) to define queries. Common query types include:

(1) Query all: Query all data, generally used for testing, for example: match_all;
(2) Full text search query (full text): Use the word segmenter to input user input The content is segmented into words, and then matched in the inverted index library. For example: match_query, multi_match_query;
(3) Precise query: Find data based on precise entry values, usually keyword, numerical, date, boolean and other types of fields. For example: ids, range, term;
(4) Geographic query (geo): query based on longitude and latitude. For example: geo_distance, geo_bounding_box;
(5) Compound query (compound): The compound condition is to combine the above various query conditions and merge the query conditions. For example: bool, function_score;

2.3.1 match_all

The match_all query syntax is as follows:

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

The code in springboot is:

   @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);
        }
    }

The running result is:
Insert image description here

2.3.2 Full text search query

Full-text search queries will segment user input content into words and are often used in search box searches. Full-text search mainly includes: match, multi_match. match will segment the user input content into words, and then search it in the inverted index database. The syntax is as follows:

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

multi_match is similar to match query, except that it allows colleagues to query multiple fields. The syntax is as follows:

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

match is used to query based on one field, while multi_match is used to query based on multiple fields (the more query fields, the worse the performance). Match should be used as much as possible in use.
The match query code in springboot is as follows:

 @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 Precise query

Precise queries generally search for keywords, values, dates, boolean and other types of fields, so the search conditions will not be segmented. Common precise queries include term and range. Term is queried based on the exact value of the term, and range is queried based on the value range.

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

Springboot uses term query code as follows:

 @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);
        }
    }

The results are as follows:
Insert image description here
The range query syntax is as follows:

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

2.3.4 Geographical query

Geographical queries are mainly based on longitude and latitude. Usage scenarios include: people near WeChat, nearby hotels, nearby businesses, etc. The query syntax is as follows:

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

2.3.5 Compound query

1. Score function
Compound query combines multiple conditions to achieve more complex searches. For example: function score (score function).
The scoring function can be used to control the relevance of the document and control the ranking of the document. The query syntax is as follows:

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等
    }
  }
}

The result is as follows:

{
    
    
  "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" : "《红高粱》"
        }
      }
    ]
  }
}

Common fractional functions include:

(1) weight: Give a specified constant value as the function result (function score);
(2) field_value_factor: Use a certain field value in the document as the function result;
(3) random_score: Randomly generate a value as the function result;
(4) script_score: Customize the calculation formula, and the formula result is used as the function result.

2. Boolean query
A Boolean query is a combination of one or more query clauses. The combination of subqueries is:

(1) must: must match every subquery, similar to "and";
(2) should: selectively match subqueries, similar to "or";
(3) must_not: must not match, does not participate in scoring, similar to "not";
(4) filter: must match, does not participate in scoring.

The query statement is as follows:

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

The query results are as follows:

{
    
    
  "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. Highlight field
The highlight field actually returns a label to the specified query field. The front end can write css styles based on this label to achieve highlighting or other purposes. The highlighted syntax is as follows:

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

The code in springboot is:

 @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);
        }
    }

The result is as follows:
Insert image description here

3. Summary

1.ik word segmenter can help es better identify Chinese patterns;
2. Pay attention to version consistency when using elasticsearch, kibana, and springboot;
3. In ES, the Restful API interface is used to request the operation of the index library and documents, and the request content is represented by DSL.

4. References

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. Appendix

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

Guess you like

Origin blog.csdn.net/qq_33479841/article/details/130369492