spring data operation ES can't smell any more

introduction

Elastic search (hereinafter referred to as ES) is not as easy to start with as mysql. Many java programmers will find the java client of ES difficult to master, although the high level client officially recommended by ES 7.x is much easier to use than the early TransportClient.

Spring Boot provides us with very convenient retrieval function support by integrating Spring Data ElasticSearch. We can operate ES almost like database. This article was written by my liver for 4 hours. I hope it can help you.

Looking at this article, I assume that I have a basic understanding of ES. For example, understand the basic concepts of index, document, and field in ES.

At the end of this article, the download address of code samples is given. You can use them at ease. My examples are all verified.

Environmental preparation

I build the 7.1.0 version of ES locally. You can start the cluster mode or single node mode. Cluster mode is a pseudo cluster simulated by different ports. The specific process is not the focus of this article.

spring boot uses version 2.3.1.RELEASE.

spring data uses version 4.0.1.RELEASE.

Example

Introduce dependency

First, maven introduces dependency. Here I introduce the latest spring data when I write this article, as follows:

<dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-elasticsearch</artifactId>
            <version>4.0.1.RELEASE</version>
        </dependency>

Here is a point to emphasize: the default version of ES client introduced by spring data elastic search in 4.0.1 is 7.6.2, while I said that the cluster is 7.1.0.

There will be a warning message to tell us that the version of the client and the server is not the same. Because we are demonstrating DEMO, it doesn't affect. But in the actual project, I suggest that you try to keep the client and server versions consistent.

Cluster configuration

Then we need to use the configuration file or configuration class to inject the client of ES cluster. The code is as follows:

@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {

    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {

        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("localhost:9200", "localhost:9201", "localhost:9202", "localhost:9203")
                .build();

        return RestClients.create(clientConfiguration).rest();
    }
}

As mentioned earlier, I use the local pseudo cluster to build the ES environment. Here, you can configure the address according to your cluster situation.

Test connectivity

I write a unit test to see if the cluster connection is normal,

@SpringBootTest(classes = {DemoApplication.class})
class DemoApplicationTests {
    @Autowired
    RestHighLevelClient highLevelClient;

    @Test
    void testESClient() throws IOException {
        GetRequest getRequest= new GetRequest("kibana_sample_data_ecommerce", "V5z1f28BdseAsPClo7bC");
        GetResponse getResponse = highLevelClient.get(getRequest, RequestOptions.DEFAULT);
        System.out.println(getResponse.getIndex());
        System.out.println(getResponse.toString());
    }

}

Simply, I read the cluster named kibana_sample_data_ecommerce index (this is an example of kibana's own e-commerce index). Query the document with ID equal to V5z1f28BdseAsPClo7bC. The test program prints the following results:

kibana_sample_data_ecommerce
{"_index":"kibana_sample_data_ecommerce","_type":"_doc","_id":"V5z1f28BdseAsPClo7bC","_version":1,"_seq_no":3,"_primary_term":1,"found":true,"_source":{"category":["Women's Shoes","Women's Clothing"],"currency":"EUR","customer_first_name":"Diane","customer_full_name":"Diane Chandler","customer_gender":"FEMALE","customer_id":22,"customer_last_name":"Chandler","customer_phone":"","day_of_week":"Sunday","day_of_week_i":6,"email":"diane@chandler-family.zzz","manufacturer":["Primemaster","Oceanavigations"],"order_date":"2020-01-26T22:58:05+00:00","order_id":584093,"products":[{"base_price":74.99,"discount_percentage":0,"quantity":1,"manufacturer":"Primemaster","tax_amount":0,"product_id":12304,"category":"Women's Shoes","sku":"ZO0360303603","taxless_price":74.99,"unit_discount_amount":0,"min_price":34.5,"_id":"sold_product_584093_12304","discount_amount":0,"created_on":"2016-12-25T22:58:05+00:00","product_name":"High heeled sandals - argento","price":74.99,"taxful_price":74.99,"base_unit_price":74.99},{"base_price":99.99,"discount_percentage":0,"quantity":1,"manufacturer":"Oceanavigations","tax_amount":0,"product_id":19587,"category":"Women's Clothing","sku":"ZO0272002720","taxless_price":99.99,"unit_discount_amount":0,"min_price":47,"_id":"sold_product_584093_19587","discount_amount":0,"created_on":"2016-12-25T22:58:05+00:00","product_name":"Classic coat - black","price":99.99,"taxful_price":99.99,"base_unit_price":99.99}],"sku":["ZO0360303603","ZO0272002720"],"taxful_total_price":174.98,"taxless_total_price":174.98,"total_quantity":2,"total_unique_products":2,"type":"order","user":"diane","geoip":{"country_iso_code":"GB","location":{"lon":-0.1,"lat":51.5},"continent_name":"Europe"}}}

ES entity mapping

We know that ES reading and writing are actually json type data, and we usually define the object entity on the client side. So the mapping between object entities and json is also a core concept.

Spring Data ES supports two entity mapping schemes:

  • Jackson Object Mapping
  • Meta Model Object Mapping

In the previous version, jackson's solution was used by default, but after 4.x, Meta Model was on top, and the former was no longer supported. So we use the second option here. First, we define an entity class and indicate the mapping relationship between it and ES entity through annotation.

@Document(indexName = "my_user")
@Data
@ToString
public class UserEsEntity implements Persistable<String> {

    @Id
    @Nullable
    private String id;

    @Field(value = "last-name", type = FieldType.Keyword)
    private String lastName;

    @Field(type = FieldType.Keyword)
    private String type;

    @Field(type = FieldType.Integer)
    private Integer age;

    @Nullable @Field(name = "birth-date", type = FieldType.Date, format = DateFormat.basic_date)
    private LocalDate birthDate;

    @Field(type = FieldType.Boolean)
    private Boolean isDeleted;
    @Field(type = FieldType.Date, format = DateFormat.basic_date)
    private LocalDate createTime;
    @Field(type = FieldType.Date, format = DateFormat.basic_date)
    private LocalDate updateTime;

    @Override
    public boolean isNew() {
        return id == null || (createTime == null);
    }
}

Here we specify the index name as my_user, the value attribute of @ Field can specify the Field name of the Field in ES. In addition, we see the date format. We can also specify the date display format. I suggest you check the official documents for more options. The address of the official document is also given at the end of this article.

Read write test

Let's try to write the index into the document first.

@Autowired
    private ElasticsearchOperations elasticsearchOperations;

    @Test
    public void testSave() {

        UserEsEntity user = new UserEsEntity();
        user.setLastName("Zhang San");
        user.setAge(29);
        user.setBirthDate(LocalDate.ofYearDay(1989, 100));
        user.setId("1");
        user.setIsDeleted(false);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());

        IndexCoordinates indexCoordinates = elasticsearchOperations.getIndexCoordinatesFor(user.getClass());

        IndexQuery indexQuery = new IndexQueryBuilder()
                .withId(user.getId())
                .withObject(user)
                .build();
        String documentId = elasticsearchOperations.index(indexQuery, indexCoordinates);
    }

Explain the following:

  • ElasticsearchOperations is an interface for spring data es to operate ES. In version 4.x, its default implementation is ElasticsearchRestTemplate. We can see this through the debug mode, as shown below:

  • indexCoordinates is a new parameter added in 4.x. with this parameter, we can specify multiple index es at the same time when operating ES.

After running this test method, we go to ES to check that the index has been written correctly, as shown in the following figure:

Let's continue to see how to write a query document. I have written several different documents with the testSave method above before querying.

@Test
    public void testQuery() {
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(new MatchAllQueryBuilder())
                .build();

        SearchHits<UserEsEntity> searchHits = searchOperations.search(searchQuery, UserEsEntity.class);
        long count = searchHits.getTotalHits();
        System.out.println(count);
        List<SearchHit<UserEsEntity>> list = searchHits.getSearchHits();
        for (SearchHit hit:list) {
            System.out.println(hit.getContent());
        }
    }

My example here is to query all documents under the index, MatchAllQueryBuilder.

Students who have used spring data know that there is a core concept in it called Repository.

Repository interface is a core interface of Spring Data. It does not provide any methods. Developers need to declare the required methods in their own defined interfaces

public interface Repository<T, ID extends Serializable> { } 

Spring Data allows us to define only interfaces, as long as we follow the specification of Spring Data, there is no need to write implementation classes. Repository has several common sub interfaces:

  • CrudRepository: inherits the Repository and implements a set of CRUD related methods
  • PagingAndSortingRepository: inheriting from CrudRepository, a set of paging sorting related methods are implemented
  • JPA Repository: inheriting pagingandsortingreposition to implement a set of JPA specification related methods

Similarly, we can use this way to operate es, so even if you don't know the client of ES, you can easily operate es. We can rewrite the above example in the way of Repository.

The first thing to do is to define your own Repository. The code is as follows:

public interface UserEsRepository extends ElasticsearchRepository<UserEsEntity, String> {
}

Yes, you are right. Only these codes, without any implementation classes, can we implement the basic addition, deletion, modification and query of ES. (of course, complex operations need to be implemented by customization)

Then we write a document,

    @Autowired
    private UserEsRepository repository;

    @Test
    public void testSave() {
        UserEsEntity entity = new UserEsEntity();
        entity.setId(UUID.randomUUID().toString());
        entity.setAge(50);
        entity.setLastName("Guiwu Dongye");
        entity.setBirthDate(LocalDate.ofYearDay(1964, 200));
        entity.setCreateTime(new Date());
        entity.setUpdateTime(new Date());
        entity.setIsDeleted(false);
        repository.save(entity);
    }

Go ahead and look at the query,

@Test
    public void testFindAll() {
        long count = repository.count();
        System.out.println(count);
        Iterable<UserEsEntity> result = repository.findAll();
        Iterator<UserEsEntity> data = result.iterator();
        while (data.hasNext()) {
            System.out.println(data.next());
        }
    }

    @Test
    public void testFindById() {
        Optional<UserEsEntity> data = repository.findById("5c7ca0b7-4236-48f1-8ed4-8ce9555092d8");
        System.out.println(data.get());
    }

Ha ha, I can't feel better! As like as two peas.

This article only introduces you to spring data es. I'm not going to explain too complex operations. I'm going to write an advanced whereabouts article later.

The code samples used in this article have been uploaded to github and can be downloaded if you are interested.

https://github.com/pony-maggi...

reference resources:

Tags: Java Spring github ElasticSearch

Posted on Sat, 27 Jun 2020 00:31:27 -0400 by pradeepss