Introduction to Elastic search to detailed explanation of cluster actual operation (native API operation and springboot integration operation) - step 2

Keywords: ElasticSearch

The previous article explained in detail how to use kibana RestFul style to operate es, followed by ES cluster, native API and SpringDataElasticsearch

https://blog.csdn.net/qq_45441466/article/details/120110968

1.Elasticsearch cluster

What are the possible problems with single point elastic search?

  • The storage capacity of a single machine is limited, and high storage cannot be realized
  • Single server is prone to single point of failure and cannot achieve high availability
  • The concurrent processing capability of a single service is limited, and high concurrency cannot be achieved

Therefore, in order to solve these problems, we need to build a cluster for elasticsearch

1.2. Cluster structure

1.2.1. Data slicing

First of all, the first problem we face is that the amount of data is too large and the amount of single point storage is limited.

What do you think should be done?

Yes, we can split the data into multiple copies, each of which is stored in different machine nodes, so as to reduce the amount of data in each node. This is the distributed storage of data, also known as data Shard.

 

 

1.2.2. Data backup

Data fragmentation solves the problem of massive data storage, but if there is a single point of failure, the fragmented data is no longer complete. How to solve this?

Yes, just like everyone will store an additional copy to the mobile hard disk in order to back up the mobile phone data. We can back up each piece of data and store it in other nodes to prevent data loss. This is data backup, also known as data replica.

Data backup can ensure high availability, but the number of nodes required will be doubled by one backup per slice, and the cost is too high!

To strike a balance between high availability and cost, we can do this:

  • Firstly, the data is partitioned and stored in different nodes
  • Then back up each partition and put it on the other node to complete mutual backup

In this way, the number of service nodes required can be greatly reduced. As shown in the figure, we take three slices and one backup for each slice as an example:

  In this cluster, if a single node fails, it will not lead to data loss, so it ensures the high availability of the cluster and reduces the amount of data storage in the node. Moreover, because multiple nodes store data, user requests will also be distributed to different servers, and the concurrency ability has been improved to a certain extent.

1.3. Building clusters

The cluster requires multiple machines. We use one machine to simulate it. Therefore, we need to deploy multiple elasticsearch nodes in one virtual machine, and the ports of each elasticsearch must be different.

One machine for simulation: copy three copies of our ES installation package, modify the port number and the different storage locations of data and log.

In actual development, each ES node is placed on a different server.

We plan to deploy three elasticsearch nodes with the cluster name of Lagou elastic:

http: indicates the port used when accessing using the http protocol. Elasticsearch head, kibana and postman. The default port number is 9200.

tcp: the communication port of each node in the cluster. It is 9300 by default

Step 1: copy and paste the es software 3 times, and rename them respectively

Step 2: modify the elasticsearch.yml under the configuration file config of each node. The first configuration file is taken as an example below

 node-01:

#Must be the ip address of this machine
network.host: 127.0.0.1

#Allow cross domain access
http.cors.enabled: true
#When cross domain is allowed, the default value is *, which means that all domain names are supported
http.cors.allow-origin: "*"
#Allow all nodes to access
#network.host: 0.0.0.0
#The name of the cluster. The cluster names of all nodes in the same cluster should be the same
cluster.name: lagou-elastic
#The current node name is different for each node
node.name: node-01
#The storage path of data is different for each node, and the storage paths of data and log corresponding to different es servers cannot be the same
path.data: D:\class\es-9201\data
#The storage path of logs is different for each node
path.logs: D:\class\es-9201\logs
#The external port of http protocol is different for each node. The default is 9200
http.port: 9201
#Each node of TCP protocol external port is different. The default value is 9300
transport.tcp.port: 9301
#The three nodes discover each other, including their own port numbers using the tcp protocol
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9301","127.0.0.1:9302","127.0.0.1:9303"]
#Declare that more than a few voting master nodes are valid. Please set it to (nodes / 2) + 1
#discovery.zen.minimum_master_nodes: 2
#Master node
#node.master: true

Step 3: start the cluster and start the three nodes separately. Don't be in a hurry. Start them one by one

Use the head plug-in to view:

 

1.4. Create index library in test cluster

Configure kibana and restart

 

After building a cluster, we need to create an index library. The problem is, when we create an index library, which service node will the data be saved to? If we slice the index library, which node will each slice be on?

You have to try it yourself.

 

Here, let's take a look at the settings of fragmentation and backup in the cluster, for example:

PUT /lagou { "settings": { "number_of_shards": 3, "number_of_replicas": 1 } }

There are two configurations:

  •   number_of_shards: the number of slices, which is set to 3 here
  • number_of_replicas: the number of replicas is set to 1 here. Each fragment has one backup and one original data. There are 2 copies in total.

Through the head view of chrome browser, we can view the storage structure of slices:

It can be seen that the test index library has three partitions, namely 0, 1 and 2. Each partition has 1 copy, a total of 6 copies.

A copy of slice 1 and slice 2 is saved on node-01

A copy of slice 0 and slice 2 is saved on node-02

Copies of partition 0 and partition 1 are saved on node-03

1.5. Working principle of cluster

1.5.1.shad and replica mechanism

(1) An index contains multiple shard s, that is, an index exists on multiple servers

(2) Each shard is a minimum unit of work and carries some data. For example, there are three servers. Now there are three pieces of data, one of which is on each of the three servers(

3) When increasing or decreasing nodes, shard will automatically load balance in nodes

(4) Primary shards and replica Shards. Each document must exist only in one primary shard and its corresponding replica shard, and cannot exist in multiple primary Shards

(5) replica shard is a replica of the primary shard. It is responsible for fault tolerance and load of read requests

(6) The number of primary shard s is fixed when the index is created, and the number of replica shard s can be modified at any time

(7) The default number of primary shards is 5, and the default number of replicas is 1 (one replica for each primary shard). By default, there are 10 shards, 5 primary shards and 5 replica Shards

(8) The primary shard cannot be placed on the same node as its own replica shard (otherwise, if the node goes down, both the primary shard and the replica will be lost, which will not play the role of fault tolerance), but it can be placed on the same node as the replica shards of other primary Shards

1.5.2. Cluster write data

1. The client selects a node to send the request. This node is the coordinating node

2. coordinating node: route the document and forward the request to the corresponding node( Select the corresponding node for storage according to a certain algorithm)

3. The primary shard on the actual node processes the request, saves the data locally, and then synchronizes the data to the replica node

4. coordinating node. If it is found that the primary node and all replica node s are completed, the request will be returned to the client

This route is simply a modular algorithm. For example, there are three servers. At this time, the id transmitted is 5, so 5% 3 = 2 is placed on the second server

1.5.3.ES query data

Inverse sorting algorithm

There is an algorithm called reverse sorting: in short, record the id of the word through word segmentation, find out which id contains the data, and then find the data according to the id

Query process

1. The client sends a request to coordinate node

2. The coordination node forwards the search request to the primary shard or replica shard corresponding to all Shards

3. query phase: each shard returns its search results (in fact, some unique identifiers) to the coordination node. The coordination node performs data consolidation, sorting, paging and other operations to produce the final results

4. fetch phase, and then the coordination node pulls data from each node according to the unique ID, and finally returns it to the client

2.Elasticsearch client

2.1. Client introduction

Clients in various languages are available on the elasticsearch official website: https://www.elastic.co/guide/en/elasticsearch/clie nt/index.html

Note: after clicking enter, select version to 6.2.4, because we used to follow version 6.2.4:

2.2. Create Demo project

2.2.1. Initialization item

Create a springboot project.

2.2.2.pom file

Note that here we directly import the SpringBoot launcher for subsequent explanation. However, it is also necessary to manually introduce the dependency of elasticsearch's high-level rest client:

<properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.8.1</version>
        </dependency>
        <!--Apache Provided by open source organizations for operation JAVA BEAN Toolkit for-->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.1</version>
        </dependency>
        <!--ES senior Rest Client-->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>6.4.3</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
            <version>6.4.3</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>6.4.3</version>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

2.2.3. Configuration file

We create application.yml under resource

2.3. Index library and mapping

  While creating the index library, we will also create the type and its mapping relationship. However, it is not recommended to use the java client for these operations. The reasons are as follows:

  • The index library and mapping are usually completed during initialization and do not require frequent operations. It is better to configure them in advance
  • The official API for creating index libraries and mapping is very cumbersome, and the json structure needs to be spliced through strings:

 

Therefore, it is recommended to implement these operations using the Rest style API we learned yesterday.

Let's take such a commodity data as an example to create an index library:

public class Product {
private Long id;
private String title; //title
private String category;// classification
private String brand; // brand
private Double price; // Price
private String images; // Picture address
}

Analyze the data structure:

  • id: it can be considered as a primary key. It is an indicator to judge whether the data is repeated in the future. It is not word segmentation. keyword type can be used
  • title: search field. Word segmentation is required. text type can be used
  • category: commodity classification. This is a whole without word segmentation. keyword type can be used. Brand: brand is similar to classification. keyword type can be used without word segmentation
  • Price: price. This is a double type
  • images: pictures, fields for display, no search, index is false, no word segmentation, and keyword type can be used

We can write such a mapping configuration:

PUT /lgt
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"item": {
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text","analyzer": "ik_max_word"
},
"category": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"images": {
"type": "keyword",
"index": false
},
"price": {
"type": "double"
}
}
}
}
}

2.4. Index data operation

With the index library, let's take a look at how to add index data

Operating MYSQL database:

1. Get database connection

2. Complete data addition, deletion, modification and query

3. Release resources

2.4.1. Initialize client

To complete any operation, you need to go through the HighLevelRestClient client. Let's see how to create it.

private static RestHighLevelClient restHighLevelClient;
    private Gson gson = new Gson();
    /**
     * Initialize client
     */
    @BeforeAll
    static void init() {
        System.out.println("Yes");
        RestClientBuilder clientBuilder = RestClient.builder(
                new HttpHost("127.0.0.1", 9201, "http"),
                new HttpHost("127.0.0.1", 9202, "http"),
                new HttpHost("127.0.0.1", 9203, "http")
        );
        restHighLevelClient = new RestHighLevelClient(clientBuilder);
    }

    /**
     * Close client
     */
    @AfterAll
    static void close() throws IOException {
        restHighLevelClient.close();

    }

2.4.2. Add a document

/**
     * New document
     * @throws IOException
     */
    @Test
    void addDoc() throws IOException {
        //1. Create a document
        Product product = new Product();
        product.setBrand("Huawei");
        product.setCategory("mobile phone");
        product.setId(1L);
        product.setImages("http://image.huawei.com/1.jpg");
        product.setTitle("Huawei P50 It's great");
        product.setPrice(88.88d);
        //2. Convert document data to json format
        String source = gson.toJson(product);
        //3. Create index request object
        //    public IndexRequest(String index, String type, String id)
        IndexRequest request = new IndexRequest("lagou","item",product.getId().toString());
        //4. Make a request
        request.source(source, XContentType.JSON);
        IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        System.out.println(response);
    }

2.4.3. Viewing documents

 /**
     * View document
     * @throws IOException
     */
    @Test
    void queryDoc() throws IOException {
        //Create the request object GetRequest and specify the id
        GetRequest request = new GetRequest("lagou","item","1");
        //Execute query
        GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
        System.out.println(response);
        String source = response.getSourceAsString();
        Product product = gson.fromJson(source, Product.class);
        System.out.println(product);
    }

2.4.4. Modify document

/**
     * Modify document
     * @throws IOException
     */
    @Test
    void updateDoc() throws IOException {
        Product product = new Product();
        product.setBrand("Huawei");
        product.setCategory("mobile phone");
        product.setId(1L);
        product.setImages("http://image.huawei.com/1.jpg");
        product.setTitle("Huawei P50 It's great");
        product.setPrice(88.99d);
        //Create the request object GetRequest and specify the id
        IndexRequest request = new IndexRequest("lagou","item",product.getId().toString());
        String source = gson.toJson(product);
        request.source(source,XContentType.JSON);
        //Execute query
        IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
        System.out.println(response);

    }

2.4.5. Delete document

/**
     * remove document
     * @throws IOException
     */
    @Test
    void deleteDoc() throws IOException {
        //Create the request object GetRequest and specify the id
        DeleteRequest request = new DeleteRequest("lagou","item","1");
        //Execute query
        DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
        System.out.println(response);
    }

2.5. Search data

2.5.1. Query all matches_ all

/**
     * Match all
     * @throws IOException
     */
    @Test
    void matchAll() throws IOException {
        //Create search object
        SearchRequest request = new SearchRequest();
        //Query build tool
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //Add query criteria and obtain various queries through QueryBuilders
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        request.source(sourceBuilder);
        //Execute query
        SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        //Get query results
        SearchHits hits = search.getHits();
        //Get file array
        SearchHit[] searchHits = hits.getHits();
        List<Product> productList = new ArrayList<>();
        for (SearchHit searchHit : searchHits) {
            String source = searchHit.getSourceAsString();
            Product product = gson.fromJson(source, Product.class);
            productList.add(product);
        }
        System.out.println(productList);
    }

Note that in the above code, the search criteria are added through sourceBuilder.query(QueryBuilders.matchAllQuery()). The parameters accepted by this query() method are: QueryBuilder interface type.

This interface provides many implementation classes corresponding to different types of queries we learned earlier, such as term query, match query, range query, boolean query, etc., as shown in the figure:

  Therefore, if we want to use different queries, it is just the different parameters passed to the sourceBuilder.query() method. These implementation classes don't need us to go to new. QueryBuilders factory is officially provided to help us build various implementation classes:

2.5.2. Keyword search match

Encapsulation basic query method:

 /**
     * Basic query method
     *         //Query build tool
     *         SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
     *         //Add query criteria and obtain various queries through QueryBuilders
     *         sourceBuilder.query(QueryBuilders.matchAllQuery());
     */
    void basicQuery(SearchSourceBuilder sourceBuilder) throws IOException {
        //Create search object
        SearchRequest request = new SearchRequest();
        request.source(sourceBuilder);
        //Execute query
        SearchResponse search = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        //Get query results
        SearchHits hits = search.getHits();
        //Get file array
        SearchHit[] searchHits = hits.getHits();
        List<Product> productList = new ArrayList<>();
        for (SearchHit searchHit : searchHits) {
            String source = searchHit.getSourceAsString();
            Product product = gson.fromJson(source, Product.class);
            productList.add(product);
        }
        System.out.println(productList);
    }
    /**
     * Keyword query
     * @throws IOException
     */
    @Test
    void match() throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //Set query criteria of query type
        builder.query(QueryBuilders.matchQuery("title","P50"));
        //Call basic query method
        basicQuery(builder);
    }

2.5.3. range query

RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price");

methodexplain
gt(Object from)greater than
gte(Object from)Greater than or equal to
lt(Object from)less than
lte(Object from)Less than or equal to

Example:

 /**
     * Range query 30-100
     * @throws IOException
     */
    @Test
    void range() throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //Set query criteria of query type
        builder.query(QueryBuilders.matchQuery("title","P50"));
        builder.query(QueryBuilders.rangeQuery("price").gt(30).lt(100));
        //Call basic query method
        basicQuery(builder);
    }

2.5.4.source filtering

_ source: store original document

By default, all data in the index library will be returned. If we want to return only some fields, we can control it through the source filter.

    /**
     * _source filter
     * @throws IOException
     */
    @Test
    void sourceFilter() throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //Set query criteria of query type
        builder.query(QueryBuilders.matchQuery("title","P50"));
        builder.query(QueryBuilders.rangeQuery("price").gt(30).lt(100));
        builder.fetchSource(new String[]{"id","price","title"},new String[0]);
        //Call basic query method
        basicQuery(builder);
    }

2.6. Sorting

It is still configured through sourceBuilder:

 

 /**
     * sort
     * @throws IOException
     */
    @Test
    void sort() throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //Set query criteria of query type
        builder.query(QueryBuilders.matchQuery("title","P50"));
        builder.query(QueryBuilders.rangeQuery("price").gt(30).lt(100));
        builder.fetchSource(new String[]{"id","price","title"},new String[0]);

        builder.sort("price", SortOrder.DESC);
        //Call basic query method
        basicQuery(builder);
    }

2.7. Paging

 

  /**
     * paging
     * @throws IOException
     */
    @Test
    void page() throws IOException {
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //Set query criteria of query type
        builder.query(QueryBuilders.matchAllQuery());
        //Add paging
        int page = 1;
        int size =3;
        int start = (page-1)*size;
        //Configure paging
        builder.from(start);
        builder.size(size);
        //Call basic query method
        basicQuery(builder);
    }

3.Spring Data Elasticsearch

Next, let's learn the elasticsearch component provided by spring: Spring Data Elasticsearch

3.1. What is spring data elastic search

Spring Data Elasticsearch (hereinafter referred to as SDE) is a sub module under the Spring Data project.

The mission of Spring Data is to provide a unified programming interface for various data access, whether it is a relational database (such as MySQL), a non relational database (such as Redis), or an index database such as Elasticsearch. So as to simplify the developer's code and improve the development efficiency.

Page of Spring Data Elasticsearch: https://projects.spring.io/spring-data-elasticsearch/

features:

  • Support Spring's @ Configuration based java Configuration mode or XML Configuration mode
  • It provides a convenient tool class ElasticsearchTemplate for operating ES. This includes implementing automatic intelligent mapping between documents and POJO s.
  • Use Spring's data transformation service to realize the function rich object mapping
  • Annotation based metadata mapping, which can be extended to support more different data formats, can define JavaBean s: class names and attributes
  • The corresponding implementation method is automatically generated according to the persistence layer interface without manual writing of basic operation code (similar to mybatis, it is automatically implemented according to the interface). Of course, manual customization query is also supported

3.2. Configure SpringDataElasticsearch

In the pom file, we introduce the initiator of SpringDataElasticsearch:

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

Then, just create a new application.yml file under resources and introduce the host and port of elasticsearch:

spring:
  data:
    elasticsearch:
      cluster-name: lagou-elastic
      cluster-nodes: 127.0.0.1:9301,127.0.0.1:9302,127.0.0.1:9303

It should be noted that the bottom layer of SpringDataElasticsearch does not use RestHighLevelClient provided by elasticsearch, but TransportClient. It does not use Http protocol for communication, but accesses the tcp port opened by elasticsearch. In our previous cluster configuration, we set 930193029303 respectively

3.3. Index library operation

Prepare a pojo object, and then prepare a new entity class as the following document corresponding to the index library:

@Data
@Document(indexName = "lat",type = "product",shards = 3,replicas = 1)
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    @Id
    private Long id;
    @Field(value = "title",type = FieldType.Text,analyzer = "ik_max_word")
    private String title; //title
    @Field(value = "category",type = FieldType.Keyword)
    private String category;// classification
    @Field(value = "brand",type = FieldType.Keyword)
    private String brand; // brand
    @Field(value = "price",type = FieldType.Double)
    private Double price; // Price
    @Field(value = "images",type = FieldType.Keyword,index = false)
    private String images; // Picture address
}
  • @Document: declare index library configuration
    • indexName: index library name
    • Type: type name. The default is "docs"
    • shards: the number of shards. The default value is 5
    • Replicas: number of replicas. The default is 1
  • @id: the id of the declared entity class
  • @Field: declare field properties
    • Type: the data type of the field
    • analyzer: Specifies the word breaker type
    • Index: create index

We first create a test class, and then inject ElasticsearchTemplate:

    @Autowired
    private ElasticsearchTemplate template;

The following is an API example for creating an index library:

    @Test
    public void createIndex() {
        //Method of creating index
        template.createIndex(Product.class);
    }

  The information that needs to be specified to create an index library, such as index library name, type name, fragmentation, number of copies, and mapping information, have been filled in

3.3.2. Create mapping

 @Test
    public void putMapping() {
        //Create type mapping
        template.putMapping(Product.class);
    }

3.4. Index data CRUD

The index data CRUD of SDE is not encapsulated in ElasticsearchTemplate, but has an interface called ElasticsearchRepository:

  We need to customize the interface and inherit elasticsearchrepository:

/**
 * @Author panghl
 * @Date 2021/9/5 0:17
 * @Description
 * When SDE accesses the index library, it needs to define an interface of persistence layer to inherit ElasticsearchRepository without implementation
 **/
public interface ProductRepository extends ElasticsearchRepository<Product, Long> {
    /**
     * Query by price range
     * @param from Start price
     * @param to End price
     * @return Eligible goods
     */
    List<Product> findByPriceBetween(Double from, Double to);
}

3.4.1. Create index data

    @Autowired
    private ProductRepository productRepository;

    @Test
    public void addDoc() {
        Product product1 = new Product(1L, "Smartisan Mobilephone", "mobile phone", "hammer", 3288.88d, "http://image.huawei.com/1.jpg");
        Product product2 = new Product(2L, "Huawei mobile phone", "mobile phone", "Huawei", 3288.88d, "http://image.huawei.com/1.jpg");
        Product product3 = new Product(3L, "Mi phones", "mobile phone", "millet", 3288.88d, "http://image.huawei.com/1.jpg");
        Product product4 = new Product(4L, "iPhone", "mobile phone", "Apple", 3288.88d, "http://image.huawei.com/1.jpg");
        Product product5 = new Product(5L, "OPPO mobile phone", "mobile phone", "OPPO", 3288.88d, "http://image.huawei.com/1.jpg");
        List<Product> productList = new ArrayList<>();
        productList.add(product1);
        productList.add(product2);
        productList.add(product3);
        productList.add(product4);
        productList.add(product5);
        productRepository.saveAll(productList);
        System.out.println("save success");
    }

3.4.2. Query index data

By default, you can query all two functions by id:

 @Test
    public void queryIndexData() {
        Product product = productRepository.findById(1L).orElse(new Product());
        //Fetch data
        //Role of orElse method: if the entity object encapsulated in optional is empty, that is, no matching document is found from the index library, return the orElse parameter
        System.out.println("product=>" + product);
    }

3.4.3. User defined method query

The query methods provided by the product repository are limited, but it provides a very powerful custom query function:

As long as we follow the syntax provided by spring data, we can define method declarations arbitrarily:

    /**
     * Query by price range
     * @param from Start price
     * @param to End price
     * @return Eligible goods
     */
    List<Product> findByPriceBetween(Double from, Double to);

There is no need to write an implementation. SDE will automatically help us implement this method. We just need to use:

    @Test
    public void querySelfIndexData() {

        List<Product> byPriceBetween = productRepository.findByPriceBetween(1000d, 4000d);
        System.out.println(byPriceBetween);
    }

Some syntax examples supported:

 

 

3.5. Native Query

  If you think the above interface still doesn't meet your needs, SDE also supports native queries. At this time, you still use ElasticsearchTemplate

The construction of query conditions is completed through a class called NativeSearchQueryBuilder, but the bottom layer of this class still uses QueryBuilders, AggregationBuilders, HighlightBuilders and other tools in the native API.

Demand: query the goods of Xiaomi mobile phone contained in the title, sort them in ascending order of price, page by page query: display 2 items per page, query page 1. Aggregate and analyze query results: obtain brands and numbers

Example

/**
     * Query the goods of Xiaomi mobile phone contained in the title, sorted in ascending order of price, and paged query: 2 items are displayed on each page, and page 1 is queried.
     * Aggregate and analyze query results: obtain brands and numbers
     */
    @Test
    public void nativeQuery() {
        //1. Build a Native Query
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //2.source come here
        //2.1 parameter: public FetchSourceFilter(String[] includes, String[] excludes)
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[0], new String[0]));
        //3. Query criteria
        queryBuilder.withQuery(QueryBuilders.matchQuery("title","Mi phones"));
        //4. Set paging and sort
        queryBuilder.withPageable(PageRequest.of(0,10, Sort.by(Sort.Direction.ASC,"price")));

        //Highlight 
//        HighlightBuilder.Field field = new HighlightBuilder.Field("title");
        HighlightBuilder builder = new HighlightBuilder();
        builder.field("title");
        builder.preTags("<font style='color:red'>");
        builder.postTags("</font>");
        //Set to 0 to return full content instead of fragments
        builder.numOfFragments(0);
        queryBuilder.withHighlightBuilder(builder);


        //5. Aggregate and analyze query results: obtain brands and numbers
        queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brand").missing("title"));


        //6. Query
        AggregatedPage<Product> result = template.queryForPage(queryBuilder.build(), Product.class,new ESSearchResultMapper());
        System.out.println(result);



        //7. Get results
        //total
        long total = result.getTotalElements();
        //Page number
        int totalPage = result.getTotalPages();
        //Get the data set of the industry
        List<Product> content = result.getContent();
        content.stream().forEach(System.out::print);

        //Get aggregate results
        Aggregations aggregations = result.getAggregations();
        Terms terms = aggregations.get("brandAgg");
        //Get the bucket and traverse the contents of the bucket
        terms.getBuckets().forEach(b->{
            System.out.println("brand->"+b.getKey());
            System.out.println("Number of documents->"+b.getDocCount());
        });

    }

Note: the above query does not support highlighted results.

Highlight:

1. Custom search result mapping

/**
 * @Author panghl
 * @Date 2021/9/5 1:09
 * @Description Custom result mapping, handling highlighting
 **/
public class ESSearchResultMapper implements SearchResultMapper {

    /**
     * Complete result mapping
     * The operation should focus on the original results:_ Take out the source and put it into the highlighted data
     *
     * @param searchResponse
     * @param aClass
     * @param pageable
     * @param <T>
     * @return AggregatedPage Three parameters are required for construction: pageable, list < Product >, and total records
     */
    @Override
    public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
        //Get total records
        SearchHits hits = searchResponse.getHits();

        long totalHits = hits.getTotalHits();
        System.out.println("Total records->" + totalHits);
        Gson gson = new Gson();
        //Record list
        List<T> productList = new ArrayList<>();
        for (SearchHit hit : hits) {
            if (hits.getHits().length <= 0) {
                return null;
            }
            //Get_ All data in the source attribute
            Map<String, Object> map = hit.getSourceAsMap();
            //Gets the highlighted field
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            //Each highlighted field needs to be set
            for (Map.Entry<String, HighlightField> highlightField : highlightFields.entrySet()) {
                //Get highlighted key: highlighted field
                String key = highlightField.getKey();
                //Get value: the effect after highlighting
                HighlightField value = highlightField.getValue();
                //Place highlighted fields and text effects in the map
                map.put(key, value.getFragments()[0].toString());
            }
            //Convert map to object
            T T = gson.fromJson(gson.toJson(map), aClass);
            productList.add(T);
        }
        //The fourth parameter, response.getAggregations(), adds the aggregation result
        return new AggregatedPageImpl<>(productList,pageable,totalHits,searchResponse.getAggregations(),searchResponse.getScrollId());
    }

    @Override
    public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
        return null;
    }


}

2. Highlight implementation:

Posted by shaunrigby on Sun, 05 Sep 2021 16:12:56 -0700