Spring Boot ElasticSearch – model DatA Layer

S

Hi, and welcome to the 5th article devoted to the theme:  “How to work with ElasticSearch using Java Spring Boot”. Previous article (Part 4: Spring Boot ElasticSearch – builder pattern and DTO search criteria object) is located here. At that article we are going to investigate the heart of our microservice – model data layer. As a reminder I am providing our architecture scheme

Search microservice architecture
Search microservice architecture

As you can guess our search has to be integrated with the ElasticSearch engine. To realize such integration it is better to look for ready solutions e.g java package that will allow us to speed up our work. Here I am going to use  spring data elasticsearch. Everything starts from data. So, at first we have to describe our data structure using objects. Let’s go to the model/elasticsearch folder and open HotelBookingDocument.java file

package com.udemy_sergii_java.spring_boot_es.model.elasticsearch;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.join.JoinField;
import java.time.Instant;
import java.util.List;

@Document(indexName = "hotels")
@Setting(shards = 1, replicas = 0)
public class HotelBookingDocument {

    @Id
    @Field(type=FieldType.Integer)
    private String id = null;

    @Field(type=FieldType.Text)
    private String name = null;

    @Field(type=FieldType.Integer)
    private Integer hotelID = null;

    @Field(type=FieldType.Text)
    private String cityNameEn = null;

    private GeoPoint location = null;

    @Field(type=FieldType.Integer)
    private Integer age = null;

    @Field(type=FieldType.Boolean)
    private Boolean freePlacesAtNow = null;

    @Field(type=FieldType.Integer)
    private Integer starts = null;

    @Field(type=FieldType.Float)
    private Double rating = null;

    @Field(type=FieldType.Nested)
    private List<Comment> comments;

    @JoinTypeRelations(
            relations =
                    {
                            @JoinTypeRelation(parent = "hotel", children = "booking")
                    }
    )
    private JoinField<String> relation;

    private Double price = null;
    @Field(type = FieldType.Date, format = DateFormat.date_time)
    private Instant createdDate = null;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String author) {
        this.name = author;
    }

    public Integer getHotelID() {
        return hotelID;
    }

    public void setHotelID(Integer hotelID) {
        this.hotelID = hotelID;
    }

    public String getCityNameEn() {
        return cityNameEn;
    }

    public void setCityNameEn(String cityNameEn) {
        this.cityNameEn = cityNameEn;
    }

    public GeoPoint getLocation() {
        return location;
    }

    public void setLocation(GeoPoint location) {
        this.location = location;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public boolean getFreePlacesAtNow() {
        return freePlacesAtNow;
    }

    public void setFreePlacesAtNow(boolean freePlacesAtNow) {
        this.freePlacesAtNow = freePlacesAtNow;
    }

    public Integer getStarts() {
        return starts;
    }

    public void setStarts(Integer starts) {
        this.starts = starts;
    }

    public double getRating() {
        return rating;
    }

    public void setRating(double rating) {
        this.rating = rating;
    }

    public List<Comment> getComments() {
        return comments;
    }

    public void setComments(List<Comment> comments) {
        this.comments = comments;
    }

    public JoinField<String> getRelation() {
        return relation;
    }

    public void setRelation(JoinField<String> relation) {
        this.relation = relation;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public Instant getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Instant createdDate) {
        this.createdDate = createdDate;
    }
}

Please, pay attention at 3 things:

  • annotations related to index- here we define our ElasticSearch index and additional essential index settings: number of replicas and shards. I will describe what that parameters at further lectures.
  • join relationship – as you remember every hotel has list of bookings. Hotel and bookings are connected as parent – child relation
  • comments property – represent elasticsearch nested objects. Below is the code for Comment class
package com.udemy_sergii_java.spring_boot_es.model.elasticsearch;

import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.time.Instant;

public class Comment {
    @Field(type=FieldType.Integer)
    private Integer hotelID = null;

    @Field(type=FieldType.Text)
    private String content = null;

    @Field(type=FieldType.Integer)
    private Integer starts = null;

    @Field(type = FieldType.Date, format = DateFormat.date_time)
    private Instant createdDate = null;

    public Integer getHotelID() {
        return hotelID;
    }

    public void setHotelID(Integer hotelID) {
        this.hotelID = hotelID;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Integer getStarts() {
        return starts;
    }

    public void setStarts(Integer starts) {
        this.starts = starts;
    }

    public Instant getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Instant createdDate) {
        this.createdDate = createdDate;
    }
}

Using object oriented approach we are representing data that would be used by ElasticSearch via according classes. Please, pay attention at class properties. Every property has according @Field annotation from elasticsearch spring data java package. Elasticsearch spring data package use that information to create proper index mapping. Here I am not discovering why exactly such architecture was chosen (parent child relationship, nested objects) or why exactly such type of mapping used for every field. If you are interested in that, please refer to my course at udemy, where I am discovering mapping and architecture aspects in details

At the next lecture (Part 6: Spring Boot ElasticSearch – initial loader, indexing test data) we will go further with creating our search microservice. Together, we will create initial loader which would be used for creating mapping for our index using our model classes. That loader will also provide indexing of some initial test data to ElasticSearch index. If you would like to pass all material more fast, then I propose you to view my on-line course at udemy where you will also find full project skeleton. Below is the link to the course. As the reader of that blog you are also getting possibility to use coupon for the best possible low price. Otherwise, please wait at next articles. Thank you for you attention.


architecture AWS cluster cyber-security devops devops-basics docker elasticsearch flask geo high availability java machine learning opensearch php programming languages python recommendation systems search systems spring boot symfony