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