How to integrate elastic search with spring boot application

We can use elastic search with spring boot application and for this below are the detailed steps which can be followed. (Please note there some compatibility issue with some version of elastic search hence choose the appropriate one as per your need, I have used the version 2.4.0 with my spring boot app version 1.4.0.RELEASE)

1. Download and unzip the elastic search from http://www.elastic.co/downloads/past-releases/elasticsearch-2-4-0

2. Open config file \elasticsearch-2.4.0\config\elasticsearch.yml and change the cluster name if needed, uncomment and add the value to property

3. Run the elastic search by clicking on batch file, \elasticsearch-2.4.0\bin\elasticsearch.bat (Here this is going to run the elastic search on separate server than your application i.e localhost:9300 )

4. Add the below dependency to your spring application

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

5. Add the below properties to application.properties file

spring.data.elasticsearch.cluster-name=
spring.data.elasticsearch.cluster-nodes=localhost:9300

6. To enable the elastic search repository which will be used to save/generate/delete the search indexes while application does any update to database. For this add below line to your spring boot application having the main method,

@EnableElasticsearchRepositories(includeFilters=@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,value=ElasticsearchRepository.class))

7. Create search repository interface against your data service (eg. below is repository that i have used to insert the post created by my app),

@Repository
public interface PostElasticsearchRepository extends ElasticsearchRepository <Post, Long> {

}

8. You must save the search indexes whenever your application inserts/update any data. Below is the sample code that I have used in my application.During the post creation , we are also inserting the post data to elastic search.


@RequestMapping(value = "/posts/create", method = RequestMethod.POST)
    	public String createPost(@Valid PostForm postForm,Model model, BindingResult bindingResult) {
    		if (bindingResult.hasErrors()) {
    			notifyService.addErrorMessage("Please fill the form correctly!");
    			return "/posts/create";
    		}
    		
    		
    		User user = null;
    		if(httpSession.getAttribute("authUserId")!=null)
    		{
    			user = userService.findById(Long.valueOf(httpSession.getAttribute("authUserId").toString()));
    		}
    		Post post = new Post(postForm.getTitle(),postForm.getBody(), user);
    		
    		postService.create(post);
    		// Generating the search index during post creation
    		searchRepo.save(post);
    		;
    		notifyService.addInfoMessage("Post created successfully!");
    		return "/posts/create";

    	}  

9. Alternatively, you can use the below code which generates and loads all the search indexes during the startup of application


@Component
public class SearchIndexLoader implements ApplicationRunner {
	@Autowired
	private PostElasticsearchRepository searchRepo;
	
	@Autowired 
	private PostService postService;
	
	@Override
	public void run(ApplicationArguments applicationArguments) throws Exception
	{
	        // Load your data and save into search repository
	        // postService is main data service of my application
		List totalPost = postService.findAll();
		searchRepo.save(totalPost);
	}
}

10. Displaying the search result on a page, this controller loads the search results from elastic search repository based on a keyword


@Controller
public class SearchController {


@Autowired
private ElasticsearchTemplate es;

@PostMapping("/search/results")
public String searchGet(Model model,@RequestParam String searchinput) {	
	 model.addAttribute("results",search(searchinput));    
     return "/search/results";
}

@GetMapping("/search/results")
public String search(Model model) {	
return "/search/results";
}


public  Map<Integer,String> search(String keyword) {
	
    SearchResponse response;
    try {
    	
    	Client client = es.getClient();
        //mypost is the cluster name on elastic search server
        response = client.prepareSearch()
                .setIndices("mypost").addFields("id", "text").addFields("title", "text")
                .setQuery(QueryBuilders.queryStringQuery(keyword)).setFrom(0).setSize(50).execute().actionGet();
        System.out.println("Total hits " + response.getHits().getTotalHits());
        
    } catch (Throwable e) {
        return new  HashMap<Integer,String>();
    }
    Map<Integer,String> result = new HashMap<Integer,String>();
    for (SearchHit hit : response.getHits()) {
        result.put(hit.field("id").getValue(),hit.field("title").getValue());
    }
    return result;
}

}

6 thoughts on “How to integrate elastic search with spring boot application

  1. Robert Ohajda says:

    Hi,
    Thanks for the post. I just wonder what will happen if during commiting transaction something fail but search entry is already saved in ES. How to handle such case?

    Regards
    Robert

    1. Ourhints says:

      Thanks Robert! We have to explicitly manage it. I would recommend to save it to elastic repository only when your db transaction is successful. As we know, Spring boot already handles the transaction management for CRUD operations in JpaRepository, you can take the advantages of it. In case you are having the custom code for CRUD operations then you can mark these custom methods to be transactional using the annotation @Transactional.

  2. Kate Karthonz says:

    Hi,

    Thank you for the guide. I have the problem where ES dependencies are creating the conflict (it seems):

    at org.elasticsearch.common.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:360) ~[elasticsearch-2.4.4.jar:2.4.4]
    at org.elasticsearch.common.inject.InjectorBuilder.injectDynamically(InjectorBuilder.java:178) ~[elasticsearch-2.4.4.jar:2.4.4]
    at org.elasticsearch.common.inject.InjectorBuilder.build(InjectorBuilder.java:110) ~[elasticsearch-2.4.4.jar:2.4.4]
    at org.elasticsearch.common.inject.Guice.createInjector(Guice.java:93) ~[elasticsearch-2.4.4.jar:2.4.4]
    at org.elasticsearch.common.inject.Guice.createInjector(Guice.java:70) ~[elasticsearch-2.4.4.jar:2.4.4]
    at org.elasticsearch.common.inject.ModulesBuilder.createInjector(ModulesBuilder.java:46) ~[elasticsearch-2.4.4.jar:2.4.4]
    at org.elasticsearch.client.transport.TransportClient$Builder.build(TransportClient.java:162) ~[elasticsearch-2.4.4.jar:2.4.4]

    I can not find the way to make it stop generating 2.4.4. I added 2.4.0 dependency and deleted all 2.4.4 from .m2 but something creates them anyways and the app throwing the error. Did you ever have similar issue?

    Thank you in advance.

    1. Ourhints says:

      Can you try after removing the version attribute from dependency once and then add back the dependency version which u want.

      1. Kate Karthonz says:

        Yes, but still shows ‘Overriding managed..’ :/ and 2.4.4 folders are again generated in m2.

        I used search and I can’t find 2.4.4 anywhere in Spring Tool Suite.

        I also tried deleting the following, but then application won’t work:

        org.springframework.boot
        spring-boot-starter-data-elasticsearch

        1. Vabhav Jain says:

          Earlier I was facing the similar issue.
          Please try with MVN clean either selecting the project and right click on the STS or MVN clean command from the CMD.

Leave a Reply to Robert Ohajda Cancel reply

Your email address will not be published. Required fields are marked *