java

Why Not Supercharge Your Java App's Search with Elasticsearch?

Unlock Superior Search Capabilities: Integrate Elasticsearch Seamlessly into Your Java Applications

Why Not Supercharge Your Java App's Search with Elasticsearch?

Elasticsearch is a game changer when it comes to implementing advanced search functionalities in Java applications. It’s powerful, scalable, and really makes searching through vast amounts of data a breeze. Below, you’ll find a friendly and informal guide on how to seamlessly integrate Elasticsearch into your Java projects, so you can start benefiting from its robust search features.

First things first, you need to set up Elasticsearch. If you’re using Docker, it’s quite straightforward. Just run:

docker run -d --name elastic-test -p 9200:9200 -e "discovery.type=single-node" -e "xpack.security.enabled=false" docker.elastic.co/elasticsearch/elasticsearch:8.9.3

Once the setup is complete, you can check if everything is running smoothly by navigating your browser to http://localhost:9200/.

Next, let’s tackle the dependencies. Assuming you’re using Maven, you need to update your pom.xml file to include the Elasticsearch Java client and Jackson Databind for JSON handling:

<dependencies>
    <dependency>
        <groupId>co.elastic.clients</groupId>
        <artifactId>elasticsearch-java</artifactId>
        <version>8.15.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.17.0</version>
    </dependency>
</dependencies>

If you are on Gradle, just add this to your build.gradle:

dependencies {
    implementation 'co.elastic.clients:elasticsearch-java:8.15.0'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0'
}

Moving on to connecting to Elasticsearch; you have to create an ElasticsearchClient. Here’s a simple example to get you started:

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.ElasticsearchTransport;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClient;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;

import java.io.IOException;

public class ElasticsearchClientExample {
    public static void main(String[] args) throws IOException {
        RestClient restClient = RestClient.builder(HttpHost.create("http://localhost:9200")).build();
        ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        ElasticsearchClient esClient = new ElasticsearchClient(transport);
    }
}

Now it’s time to make your data searchable by indexing it. Here’s an example of how to index a document:

import co.elastic.clients.elasticsearch.core.IndexResponse;
import java.io.IOException;

public class IndexDocumentExample {
    public static void main(String[] args) throws IOException {
        // Assuming you have an Elasticsearch client instance
        ElasticsearchClient esClient = new ElasticsearchClient(transport);

        Person person = new Person(30, "John Doe", new Date());
        
        IndexResponse response = esClient.index(i -> i
                .index("people")
                .id(person.getFullName())
                .document(person));

        System.out.println("Indexed with version " + response.version());
    }
}

class Person {
    private int age;
    private String name;
    private Date birthDate;

    public Person(int age, String name, Date birthDate) {
        this.age = age;
        this.name = name;
        this.birthDate = birthDate;
    }

    public String getFullName() {
        return name;
    }
}

Searching for documents is where Elasticsearch shines. Let’s start with a simple match query:

import co.elastic.clients.elasticsearch.core.search.SearchResponse;
import java.io.IOException;

public class SearchDocumentsExample {
    public static void main(String[] args) throws IOException {
        ElasticsearchClient esClient = new ElasticsearchClient(transport);
        String searchText = "John";

        SearchResponse<Person> response = esClient.search(s -> s
                .index("people")
                .query(q -> q
                        .match(t -> t
                                .field("name")
                                .query(searchText)
                        )
                ), Person.class);

        var totalHits = response.hits().total();
        if (totalHits.relation() == TotalHitsRelation.Eq) {
            System.out.println("There are " + totalHits.value() + " results");
        } else {
            System.out.println("There are more than " + totalHits.value() + " results");
        }

        response.hits().hits().forEach(hit -> {
            Person person = hit.source();
            System.out.println("Found person " + person.getFullName() + ", score " + hit.score());
        });
    }
}

If you need a more advanced search, Elasticsearch can handle fuzzy queries, perfect for dealing with typos. Check this out:

import co.elastic.clients.elasticsearch.core.search.SearchResponse;
import java.io.IOException;

public class FuzzySearchExample {
    public static void main(String[] args) throws IOException {
        ElasticsearchClient esClient = new ElasticsearchClient(transport);
        String searchText = "Jhon";

        SearchResponse<Person> response = esClient.search(s -> s
                .index("people")
                .query(q -> q
                        .match(t -> t
                                .field("name")
                                .fuzziness("AUTO")
                                .query(searchText)
                        )
                ), Person.class);

        var totalHits = response.hits().total();
        if (totalHits.relation() == TotalHitsRelation.Eq) {
            System.out.println("There are " + totalHits.value() + " results");
        } else {
            System.out.println("There are more than " + totalHits.value() + " results");
        }

        response.hits().hits().forEach(hit -> {
            Person person = hit.source();
            System.out.println("Found person " + person.getFullName() + ", score " + hit.score());
        });
    }
}

Ever thought about using search templates? These make it super easy to reuse search queries with different parameters. Here’s a sample:

import co.elastic.clients.elasticsearch.core.search.SearchTemplateResponse;
import co.elastic.clients.json.JsonData;
import java.io.IOException;

public class SearchTemplateExample {
    public static void main(String[] args) throws IOException {
        ElasticsearchClient esClient = new ElasticsearchClient(transport);

        esClient.putScript(r -> r
                .id("query-script")
                .script(s -> s
                        .lang("mustache")
                        .source("{\"query\":{\"match\":{\"{{field}}\":\"{{value}}\"}}}")
                ));

        SearchTemplateResponse<Person> response = esClient.searchTemplate(r -> r
                .index("people")
                .id("query-script")
                .params("field", JsonData.of("name"))
                .params("value", JsonData.of("John")), Person.class);

        response.hits().hits().forEach(hit -> {
            Person person = hit.source();
            System.out.println("Found person " + person.getFullName() + ", score " + hit.score());
        });
    }
}

Integrating Elasticsearch with your application means ensuring that your data-fetching routines are updated to pull from Elasticsearch instead of your database. This involves a couple of key steps:

  1. Index Your Data: Keep your data in Elasticsearch up-to-date by syncing it periodically or indexing in real-time as updates happen.

  2. Modify Your Search Service: Update your existing search service to use the Elasticsearch Java client.

  3. Handle Search Requests: Forward search requests from your UI to your search service, which then queries Elasticsearch.

  4. Display Results: Map Elasticsearch hits to your app’s data model and show them in the UI.

Here’s a simple example of a possible search service implementation:

public class SearchService {
    private final ElasticsearchClient esClient;

    public SearchService(ElasticsearchClient esClient) {
        this.esClient = esClient;
    }

    public List<Person> searchPeople(String name) throws IOException {
        SearchResponse<Person> response = esClient.search(s -> s
                .index("people")
                .query(q -> q
                        .match(t -> t
                                .field("name")
                                .query(name)
                        )
                ), Person.class);

        List<Person> people = new ArrayList<>();
        response.hits().hits().forEach(hit -> people.add(hit.source()));
        return people;
    }
}

To wrap things up, Elasticsearch can really level up your application’s search capabilities. The steps and examples here should help you get started with integration. Whether you’re managing simple search requests or tackling more complex queries, Elasticsearch promises a powerful and efficient solution to meet your needs. So dive in and explore the endless possibilities Elasticsearch offers for your Java applications!

Keywords: Elasticsearch, Java, advanced search functionalities, scalable, Docker, Maven, Gradle, indexing documents, search queries, search templates



Similar Posts
Blog Image
Securing Microservices Frontends with Vaadin and OAuth2

Microservices security with Vaadin and OAuth2: server-side UI, authentication protocol. Combine for frontend security. Use tokens for backend communication. Implement JWT, service-to-service auth. Regular updates and holistic security approach crucial.

Blog Image
Advanced Java Logging: Implementing Structured and Asynchronous Logging in Enterprise Systems

Advanced Java logging: structured logs, asynchronous processing, and context tracking. Use structured data, async appenders, MDC for context, and AOP for method logging. Implement log rotation, security measures, and aggregation for enterprise-scale systems.

Blog Image
10 Essential Java Features Since Version 9: Boost Your Productivity

Discover 10 essential Java features since version 9. Learn how modules, var, switch expressions, and more can enhance your code. Boost productivity and performance now!

Blog Image
7 Shocking Java Facts That Will Change How You Code Forever

Java: versatile, portable, surprising. Originally for TV, now web-dominant. No pointers, object-oriented arrays, non-deterministic garbage collection. Multiple languages run on JVM. Adaptability and continuous learning key for developers.

Blog Image
Master Java’s Foreign Function Interface (FFI): Unleashing C++ Code in Your Java Projects

Java's Foreign Function Interface simplifies native code integration, replacing JNI. It offers safer memory management and easier C/C++ library usage. FFI opens new possibilities for Java developers, combining Java's ease with C++'s performance.

Blog Image
Unlock the Magic of Custom Spring Boot Starters

Crafting Consistency and Reusability in Spring Boot Development