Spring Boot Integration with Elasticsearch

Spring Boot Integration with Elasticsearch

I. Introduction

In modern applications, search functionality is a crucial requirement. Open-source Elasticsearch has become the preferred full-text search engine due to its ability to quickly store, search, and analyze massive amounts of data. Spring Boot provides a convenient search function through Spring Data Elasticsearch, which integrates with Elasticsearch’s distributed search service based on the underlying Lucene.

II. Installation of Elasticsearch

To start with Elasticsearch, we use Docker to install it. We pull the Elasticsearch image using the command docker pull elasticsearch. To allocate 2G of memory, we use the command docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name es01 5c1e1ecfe33a. We can access the Elasticsearch instance at http://127.0.0.1:9200.

III. Understanding Elasticsearch Concepts

In Elasticsearch, a document is stored as an example, such as employee data. The data is stored in an index, which is a cluster of indices. Each index contains a plurality of types, and each type stores multiple documents with various attributes. The relationship between these concepts is similar to a database, where an index is like a database, a type is like a table, and a document is like a record in the table.

IV. Integration of Elasticsearch with Spring Boot

To integrate Elasticsearch with Spring Boot, we create a project called springboot-elasticsearch with web support. Spring Boot provides two ways to operate Elasticsearch: Jest and Spring Data. We will explore both methods.

Jest Operation with Elasticsearch

Jest is an Elasticsearch Java HTTP REST client. It fills the gaps in the official Elasticsearch Java API. We create a pom.xml file to manage dependencies, including Jest and Spring Boot Starter Data Elasticsearch.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.gf</groupId>
    <artifactId>springboot-elasticsearch</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>springboot-elasticsearch</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>io.searchbox</groupId>
            <artifactId>jest</artifactId>
            <version>5.3.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

We create a Application.properties file to configure the Elasticsearch connection.

spring.elasticsearch.jest.uris=http://127.0.0.1:9200

We create an Article class to represent a document.

package com.gf.entity;

import io.searchbox.annotations.JestId;
import lombok.ToString;

@ToString
public class Article {
    @JestId
    private Integer id;
    private String author;
    private String title;
    private String content;

    // getters and setters
}

We create a test class to demonstrate Jest operation with Elasticsearch.

package com.gf;

import com.gf.entity.Article;
import io.searchbox.client.JestClient;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootElasticsearchApplicationTests {

    @Autowired
    private JestClient jestClient;

    @Test
    public void createIndex() {
        // 1. Create an Article object
        Article article = new Article();
        article.setId(1);
        article.setTitle("Good News");
        article.setAuthor("Joe Smith");
        article.setContent("Hello World");

        // 2. Build an index
        Index index = new Index.Builder(article)
                .index("gf")
                .type("news")
                .build();

        try {
            // 3. Execute the index operation
            jestClient.execute(index);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void search() {
        // 1. Create a search query
        String query = "{\n" +
                "\"Query\": {\n" +
                "\"Match\": {\n" +
                "\"Content\": \"hello\"\n" +
                "}\n" +
                "}\n" +
                "}";

        // 2. Build a search object
        Search search = new Search.Builder(query)
                .addIndex("gf")
                .addType("news")
                .build();

        try {
            // 3. Execute the search operation
            SearchResult result = jestClient.execute(search);
            System.out.println(result.getJsonString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Spring Data Operation with Elasticsearch

Spring Data Elasticsearch provides a more convenient way to interact with Elasticsearch. We create a Book class to represent a document.

package com.gf.entity;

import org.springframework.data.elasticsearch.annotations.Document;
import lombok.ToString;

@Document(indexName = "gf", type = "book")
@ToString
public class Book {
    private Integer id;
    private String bookName;
    private String author;

    // getters and setters
}

We create a BookRepository interface to define the repository operations.

package com.gf.repository;

import com.gf.entity.Book;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;

public interface BookRepository extends ElasticsearchRepository<Book, Integer> {
    List<Book> findByBookNameLike(String bookName);
}

We create a test class to demonstrate Spring Data operation with Elasticsearch.

package com.gf;

import com.gf.entity.Article;
import com.gf.entity.Book;
import com.gf.repository.BookRepository;
import io.searchbox.client.JestClient;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootElasticsearchApplicationTests {

    @Autowired
    private BookRepository bookRepository;

    @Test
    public void createIndex2() {
        Book book = new Book();
        book.setId(1);
        book.setBookName("Journey to the West");
        book.setAuthor("Wu Cheng-en");
        bookRepository.index(book);
    }

    @Test
    public void useFind() {
        List<Book> list = bookRepository.findByBookNameLike("tour");
        for (Book book : list) {
            System.out.println(book);
        }
    }
}

However, we encounter an error due to the version conflict between Spring Data Elasticsearch and Elasticsearch. We refer to the official adaptation table provided by Spring Data Elasticsearch and find that we need to use version 6.2.2 of Spring Data Elasticsearch and version 6.5.1 of Elasticsearch.

We update our Docker image to use version 6.5.1 of Elasticsearch.

docker pull elasticsearch:6.5.1
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name es02 5c1e1ecfe33a

We update our application.properties file to configure the Elasticsearch connection.

spring.data.elasticsearch.cluster-name=docker-cluster
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300

We test again and find that the test can be passed. We visit http://127.0.0.1:9200/gf/book/1 and can get the information into the index.