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
Spring Boot Data Magic: Mastering Multiple Databases Without the Headache

Navigating the Labyrinth of Multiple Data Sources in Spring Boot for Seamless Integration

Blog Image
Spring Meets JUnit: Crafting Battle-Ready Apps with Seamless Testing Techniques

Battle-Test Your Spring Apps: Integrate JUnit and Forge Steel-Clad Code with Mockito and MockMvc as Your Trusted Allies

Blog Image
Master Java Time API: Prevent Time Zone Bugs and Handle Temporal Logic Like a Pro

Master Java time handling with modern java.time API. Learn time zones, durations, parsing & business day calculations. Avoid DST bugs & legacy Date issues. Click for expert tips!

Blog Image
Phantom Types in Java: Supercharge Your Code with Invisible Safety Guards

Phantom types in Java add extra compile-time information without affecting runtime behavior. They're used to encode state, units of measurement, and create type-safe APIs. This technique improves code safety and expressiveness, but can increase complexity. Phantom types shine in core libraries and critical applications where the added safety outweighs the complexity.

Blog Image
Keep Your Services Smarter with Micronaut API Versioning

Seamlessly Upgrade Your Microservices Without Breaking a Sweat

Blog Image
Advanced Java Performance Tuning Techniques You Must Know!

Java performance tuning optimizes code efficiency through profiling, algorithm selection, collection usage, memory management, multithreading, database optimization, caching, I/O operations, and JVM tuning. Measure, optimize, and repeat for best results.