Spring Boot Elasticsearch

Spring Data has a starter for Elasticsearch that takes away the boilerplate code of configuring Elasticsearch to work with Spring.

First create a springboot application.

Include this dependency to import Elasticsearch dependencies.

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

 

Configure Elasticsearch.

You can change the host url(localhost:9200) to point to your respective environment like dev, qa, or production when needed.
Make sure to use @EnableElasticsearchRepositories on the configuration class. Also it is important to specify the package where your Elasticsearch repositories are in. Springboot needs to know that so that it cans and configures those repositories and their indexes.

@Configuration
@EnableElasticsearchRepositories(basePackages = "com.lovemesomecoding.es.repository")
public class ElasticsearchConfig {

    @Bean
    public RestHighLevelClient client() {
        ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo("localhost:9200").build();
        return RestClients.create(clientConfiguration).rest();
    }

    @Bean
    public ElasticsearchOperations elasticsearchTemplate() {
        return new ElasticsearchRestTemplate(client());
    }
}

 

Create your index repository using ElasticsearchRepository.

package com.lovemesomecoding.es.repository;

import java.util.List;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import com.lovemesomecoding.es.user.User;

public interface UserRepository extends ElasticsearchRepository<User, Long> {

    List<User> findByLastName(String lastName);
}

 

Create your index

I prefer using Spring Data annotations to create and manage my index. This allows Spring Data to manage index mapping, field type, etc which is time consuming I were to do it myself. When your application boots up it will automatically create or update your index and its mapping based on your Index class. Without this you would have to manually create/update index when there are new fields to add.

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(value = Include.NON_NULL)
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
@Document(indexName = "sb_with_es_user")
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    private String            id;

    @MultiField(mainField = @Field(type = FieldType.Keyword), otherFields = {@InnerField(suffix = "token", type = FieldType.Text)})
    private String            firstName;

    @MultiField(mainField = @Field(type = FieldType.Keyword), otherFields = {@InnerField(suffix = "token", type = FieldType.Text)})
    private String            lastName;

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

    @MultiField(mainField = @Field(type = FieldType.Keyword), otherFields = {@InnerField(suffix = "token", type = FieldType.Text)})
    private String            email;

}

Create your DAO(Data Access Object).

It is best practice to have a DAO per index so that you can isolate changes and your code is readable. Having a DAO allows you to wire in your repository(UserRepository) and RestHighLevelClient. With your repository you can take advantage of what Spring Data has developed for you to use. You also has RestHighLevelClient that you can use as a pure java client to query Elasticsearch.

@Repository
@Slf4j
public class UserDAOImp implements UserDAO {

    @Autowired
    private UserRepository      userRepository;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public List<User> getAllUsers() {

        return null;
    }

    @Override
    public List<User> getUsersByFirstName(String firstName) {
        int pageNumber = 0;
        int pageSize = 10;

        SearchRequest searchRequest = new SearchRequest("sb_with_es_user");
        searchRequest.allowPartialSearchResults(true);
        searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(pageNumber * pageSize);
        searchSourceBuilder.size(pageSize);
        searchSourceBuilder.timeout(new org.elasticsearch.core.TimeValue(60, TimeUnit.SECONDS));

        /**
         * fetch only a few fields
         */
        // searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""});

        /**
         * Query
         */

        /**
         * Filter<br>
         * term query looks for exact match. Use keyword
         */

        searchSourceBuilder.query(QueryBuilders.termQuery("firstName", firstName));

        searchRequest.source(searchSourceBuilder);

        searchRequest.preference("firstName");

        if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) {
            log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString());
        } else {
            log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString());
        }
        List<User> users = null;
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            log.info("isTimedOut={}, totalShards={}, totalHits={}", searchResponse.isTimedOut(), searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value);

            users = getResponseResult(searchResponse.getHits());

            log.info(ObjectUtils.toJson(users));

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return users;
    }

    private List<User> getResponseResult(SearchHits searchHits) {

        Iterator<SearchHit> it = searchHits.iterator();

        List<User> searchResults = new ArrayList<>();

        while (it.hasNext()) {
            SearchHit searchHit = it.next();
            // log.info("sourceAsString={}", searchHit.getSourceAsString());
            try {

                User obj = ObjectUtils.getObjectMapper().readValue(searchHit.getSourceAsString(), new TypeReference<User>() {});
                // log.info("obj={}", ObjectUtils.toJson(obj));

                searchResults.add(obj);
            } catch (IOException e) {
                log.warn("IOException, msg={}", e.getLocalizedMessage());
            }
        }

        return searchResults;

    }

    @Override
    public List<User> getUsersByLastName(String lastName) {
        return userRepository.findByLastName(lastName);
    }
}

 

 

Source code on Github

Official Documentation




Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Leave a Reply

Your email address will not be published.