{{announcement.body}}
{{announcement.title}}

Elastic Search @ 6.4.3

DZone 's Guide to

Elastic Search @ 6.4.3

In this article, we discuss how to write a simple Java service to expose a specific computation on top of an elastic search query.

· Big Data Zone ·
Free Resource

rubber-band-being-flicked

Elastic - 6.4.3

Working with Elastic is quite fun. You can simply use a query string URL or compose a Java program using the Java high-level/low-level client to consume and return the results.

In this blog, I am going to write a simple Java service that will expose a specific computation on top of an elastic search query. You can read more at the elastic.co

I will write a simple Sprint Boot application that will expose a few endpoints, allowing users to invoke queries on a predefined index, which can be customized.

You may also like: Hunting the ELK (Stack): Data Monitoring to Visualization

Step 1: Dependency Check

The version and the libraries I am using are:

  • Dependencies Java-JDK 1.8 (AdoptOpenJDK).
  • Maven 3.
  • Spring Boot — 2.2.1. RELEASE.

One might question why you'd want to write a service on top of a service. Well, in my case, I wanted to run a specific query that had to be exposed as a URL instead of a request body, and unfortunately, the version that I had to work with was 6.4.3, not the latest 7.4, which could have made this process much simpler.

Objective

Expose two endpoints as REST endpoints (unsecured):

  •  /es: Returns customized information about the cluster and the version of ES.

  •  /metric: Returns the response of my customized query (more information below).

Step 2: Spring.io Starter

As with most of my sample blogs, I start at https://start.spring.io/ to create the project space.

POM.XML

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<packaging>war</packaging>
<groupId>net.my.ops</groupId>
<artifactId>metrics</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>metrics</name>
<description>Metrics counter</description>

<properties>
<java.version>1.8</java.version>
<spring.boot.version>2.0.0.RELEASE</spring.boot.version>
<maven.compiler.sources>1.8</maven.compiler.sources>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>


<build>
<finalName>${artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</plugin>
</plugins>
</build>

</project>


We need to add the RestHighLevelClient dependency in the POM.xml file.

       <dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elastic.version}</version>
</dependency>

<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elastic.version}</version>
</dependency>
<properties>
   <elastic.version>6.4.3</elastic.version>
</properties>


With pom defined it was time to start writing my code. I did add some debug details to check all the beans that will be created when I boot up my application. More information about CommandLineRunner can be found here.

@SpringBootApplication
public class MetricsApplication {

public static void main(String[] args) {
SpringApplication.run(MetricsApplication.class, args);
}

@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {

System.out.println("Let's inspect the beans provided by Spring Boot:");

String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}

};
}

}


The first thing I did was add an application.properties for my project that contains the hostname and the required index information

elasticsearch.host=myhostname.samarthya.me
metrics.index=my_alarm_index_1_1



Step 3: New Objects

After defining the properties, it is time to create the configuration object that will construct the high-level client of ES.

@Configuration
public class ElasticsearchConfig {

    @Value("${elasticsearch.host}")
    private String elasticsearchHost;

    @Value("${metrics.index}")
    private String indexName;

    @Bean(destroyMethod = "close")
    public RestHighLevelClient client() {

        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost(elasticsearchHost)));

        return client;

    }

    @Bean()
    public SearchRequest searchRequest()
    {
        SearchRequest searchRequest = new SearchRequest(indexName);
        return searchRequest;
    }

}


It is a simple definition for creating the RestHighLevelClient and the SearchRequest. More details are available on the website elastic.co.

The basic building blocks are available, so, now, it's time to define a service that will invoke the search on ES when invoked.

@Service
public class MetricService {
    private RestHighLevelClient restClient;
    private SearchRequest searchRequest;

    @Autowired
    public MetricService(RestHighLevelClient restClient, SearchRequest searchRequest) {
        this.restClient = restClient;
        this.searchRequest = searchRequest;
    }

    /** Cluster information as read from the response **/
    public String clusterInfo() {
         try{
              MainResponse response = restClient.info(RequestOptions.DEFAULT);

              XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
                      .field("name",response.getClusterName().toString())
                      .field("version", response.getVersion().toString())
                      .endObject();
              return Strings.toString(builder);
         }catch (IOException ex){
              System.out.println(ex.getMessage());
         }

         return "ERROR: 500";

    }

    /** Metric information that is composite query which has Must and Should **/
    public String getMetric() {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchSourceBuilder.size(1);
        searchSourceBuilder.sort(new FieldSortBuilder("@timestamp").order(SortOrder.DESC));
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();



        List<QueryBuilder> mustQueries = new LinkedList<QueryBuilder>();

        mustQueries.add(QueryBuilders.termQuery("header_item_for", "users"));
        mustQueries.add(QueryBuilders.termQuery("metric_name", "SuccessfulLogin"));


        boolQueryBuilder.must().add(QueryBuilders.rangeQuery("timestamp").gt("now-5m/m").lt("now/m"));
        boolQueryBuilder.should().addAll(mustQueries);

        searchSourceBuilder.query(boolQueryBuilder);
        System.out.println(boolQueryBuilder.toString());
        this.searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = null;
        String output = null;
        try {
            searchResponse = restClient.search(this.searchRequest, RequestOptions.DEFAULT);

            if (searchResponse != null) {
                System.out.println(" results found.");
                output = searchResponse.toString();
            }
        } catch (ElasticsearchStatusException ex) {
            System.out.println(ex.getMessage());
        }
        catch (IOException ex) {
            System.out.println(" Ex : " + ex.getMessage());
        }
        return output;
    }

}


The two methods defined here are meant to be used by the two rest endpoints, that I will be exposing.

clusterInfo()

It returns the ES version information as a JSON with two information fields, name and version.

getMetric()

It returns a single document that has the header_item_for as users and metric_name as  SuccessfulLogin, which are the fields in the document. The other limiting condition is that the document should have been generated within the time-window of the last 7 minutes (from now).

Step 4:  Controller


@RestController
public class MetricController {


    private MetricService metricService;

    @Autowired
    public MetricController(MetricService metricService) {
        this.metricService = metricService;
    }

    @RequestMapping("/")
    public ServiceVersion defaultHandler() {
        return getVersionInformation();
    }

    @RequestMapping("/metric")
    public String ElasticQuery() {
        return metricService.getMetric();
    }

    @RequestMapping("/es")
    public String getVersionInformation() {
        return metricService.clusterInfo();
    }
}


The controller ties the endpoint to the service endpoints exposed. If I ran the application (and the index exists), I can see the results of my search query.

To summarize, I just walked through a sample program by which you can execute queries (using QueryBuilder and SearchRequest) and the Java High Level Client (v6.4) to deploy your simple rest services on Tomcat.


Further Reading

Topics:
elastic ,springboot ,spring data ,java ,elastisearch ,elk ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}