BAEL-6538: Geospatial Support in ElasticSearch (#14554)

* Migrated to spring-boot and created the first working test.

* JAVA-6538: Refactor Java GeoQueries to Java Client for Ealastic.
This commit is contained in:
Harry9656
2023-09-09 21:37:16 +02:00
committed by GitHub
parent 4609814e3c
commit 677f8ae2d0
6 changed files with 292 additions and 286 deletions
@@ -8,77 +8,56 @@
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-spring-5</artifactId>
<artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-spring-5</relativePath>
<relativePath>../../parent-boot-3</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>${spring-data-elasticsearch.version}</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--These two dependencies are inherited from spring-data-elasticsearch. Overriding to the newer version
will break tests. -->
<!-- <dependency>-->
<!-- <groupId>co.elastic.clients</groupId>-->
<!-- <artifactId>elasticsearch-java</artifactId>-->
<!-- <version>${elasticsearch.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>com.fasterxml.jackson.core</groupId>-->
<!-- <artifactId>jackson-databind</artifactId>-->
<!-- <version>${jackson.version}</version>-->
<!-- </dependency>-->
<!-- Here for backward compatibility-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.17.11</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<dependency>
<groupId>org.locationtech.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
<version>${spatial4j.version}</version>
</dependency>
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>${jts.version}</version>
<exclusions>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<properties>
<spring-data-elasticsearch.version>5.1.2</spring-data-elasticsearch.version>
<elasticsearch.version>8.9.0</elasticsearch.version>
<fastjson.version>2.0.37</fastjson.version>
<spatial4j.version>0.8</spatial4j.version>
<jts.version>1.18.2</jts.version>
<jackson.version>2.15.2</jackson.version>
</properties>
@@ -0,0 +1,12 @@
package com.baeldung;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@@ -0,0 +1,16 @@
package com.baeldung.elasticsearch;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Location {
private String name;
private List<Double> location;
}
@@ -12,8 +12,6 @@ import java.util.Date;
public class Person {
private int age;
private String fullName;
private Date dateOfBirth;
}
@@ -15,18 +15,18 @@ import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.StringReader;
import java.util.Date;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* This Manual test requires: Elasticsearch instance running on localhost:9200.
@@ -35,41 +35,35 @@ import static org.junit.Assert.assertEquals;
* 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.0
*/
@Slf4j
@Disabled("Manual test")
public class ElasticSearchManualTest {
private ElasticsearchClient client = null;
@Before
@BeforeEach
public void setUp() throws IOException {
RestClient restClient = RestClient
.builder(HttpHost.create("http://localhost:9200"))
.build();
RestClient restClient = RestClient.builder(HttpHost.create("http://localhost:9200"))
.build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
client = new ElasticsearchClient(transport);
Person person1 = new Person(10, "John Doe", new Date());
Person person2 = new Person(25, "Janette Doe", new Date());
Person person3 = new Person(8, "Mark Doe", new Date());
client.index(builder -> builder
.index("person")
.id(person1.getFullName())
.document(person1));
client.index(builder -> builder
.index("person")
.id(person2.getFullName())
.document(person2));
client.index(builder -> builder
.index("person")
.id(person3.getFullName())
.document(person3));
client.index(builder -> builder.index("person")
.id(person1.getFullName())
.document(person1));
client.index(builder -> builder.index("person")
.id(person2.getFullName())
.document(person2));
client.index(builder -> builder.index("person")
.id(person3.getFullName())
.document(person3));
}
@Test
public void givenJsonDocument_whenJavaObject_thenIndexDocument() throws Exception {
Person person = new Person(20, "Mark Doe", new Date(1471466076564L));
IndexResponse response = client.index(i -> i
.index("person")
.id(person.getFullName())
.document(person));
IndexResponse response = client.index(i -> i.index("person")
.id(person.getFullName())
.document(person));
log.info("Indexed with version: {}", response.version());
assertEquals(Result.Created, response.result());
@@ -81,10 +75,9 @@ public class ElasticSearchManualTest {
public void givenJsonString_whenJavaObject_thenIndexDocument() throws Exception {
String jsonString = "{\"age\":10,\"dateOfBirth\":1471466076564,\"fullName\":\"John Doe\"}";
StringReader stringReader = new StringReader(jsonString);
IndexResponse response = client.index(i -> i
.index("person")
.id("John Doe")
.withJson(stringReader));
IndexResponse response = client.index(i -> i.index("person")
.id("John Doe")
.withJson(stringReader));
log.info("Indexed with version: {}", response.version());
assertEquals("person", response.index());
assertEquals("John Doe", response.id());
@@ -93,9 +86,8 @@ public class ElasticSearchManualTest {
@Test
public void givenDocumentId_whenJavaObject_thenDeleteDocument() throws Exception {
String documentId = "Mark Doe";
DeleteResponse response = client.delete(i -> i
.index("person")
.id(documentId));
DeleteResponse response = client.delete(i -> i.index("person")
.id(documentId));
assertEquals(Result.Deleted, response.result());
assertEquals("Mark Doe", response.id());
}
@@ -103,24 +95,23 @@ public class ElasticSearchManualTest {
@Test
public void givenSearchRequest_whenMatch_thenReturnAllResults() throws Exception {
String searchText = "John";
SearchResponse<Person> searchResponse = client.search(s -> s
.index("person")
.query(q -> q
.match(t -> t
.field("fullName")
.query(searchText))), Person.class);
SearchResponse<Person> searchResponse = client.search(s -> s.index("person")
.query(q -> q.match(t -> t.field("fullName")
.query(searchText))), Person.class);
List<Hit<Person>> hits = searchResponse.hits().hits();
List<Hit<Person>> hits = searchResponse.hits()
.hits();
assertEquals(1, hits.size());
assertEquals("John Doe", hits.get(0).source().getFullName());
assertEquals("John Doe", hits.get(0)
.source()
.getFullName());
}
@Test
public void givenGetRequest_whenMatch_thenReturnAllResults() throws IOException {
String documentId = "John Doe";
GetResponse<Person> getResponse = client.get(s -> s
.index("person")
.id(documentId), Person.class);
GetResponse<Person> getResponse = client.get(s -> s.index("person")
.id(documentId), Person.class);
Person source = getResponse.source();
assertEquals("John Doe", source.getFullName());
}
@@ -128,47 +119,58 @@ public class ElasticSearchManualTest {
@Test
public void givenSearchRequest_whenMatchAndRange_thenReturnAllResults() throws Exception {
String searchText = "John";
SearchResponse<Person> searchResponse = client.search(s -> s
.index("person")
.query(q -> q
.match(t -> t
.field("fullName").query(searchText)))
.query(q -> q
.range(range -> range
.field("age").from("1").to("10"))),
Person.class);
SearchResponse<Person> searchResponse = client.search(s -> s.index("person")
.query(q -> q.match(t -> t.field("fullName")
.query(searchText)))
.query(q -> q.range(range -> range.field("age")
.from("1")
.to("10"))), Person.class);
List<Hit<Person>> hits = searchResponse.hits().hits();
List<Hit<Person>> hits = searchResponse.hits()
.hits();
assertEquals(1, hits.size());
assertEquals("John Doe", hits.get(0).source().getFullName());
assertEquals("John Doe", hits.get(0)
.source()
.getFullName());
}
@Test
public void givenMultipleQueries_thenReturnResults() throws Exception {
Query ageQuery = RangeQuery.of(r -> r.field("age").from("5").to("15"))._toQuery();
SearchResponse<Person> response1 = client.search(s -> s.query(q -> q.bool(b -> b
.must(ageQuery))), Person.class);
response1.hits().hits().forEach(hit -> log.info("Response 1: {}", hit.source()));
Query ageQuery = RangeQuery.of(r -> r.field("age")
.from("5")
.to("15"))
._toQuery();
SearchResponse<Person> response1 = client.search(s -> s.query(q -> q.bool(b -> b.must(ageQuery))), Person.class);
response1.hits()
.hits()
.forEach(hit -> log.info("Response 1: {}", hit.source()));
Query fullNameQuery = MatchQuery.of(m -> m.field("fullName").query("John"))._toQuery();
SearchResponse<Person> response2 = client.search(s -> s.query(q -> q.bool(b -> b
.must(fullNameQuery))), Person.class);
response2.hits().hits().forEach(hit -> log.info("Response 2: {}", hit.source()));
Query doeContainsQuery = SimpleQueryStringQuery.of(q -> q.query("*Doe"))._toQuery();
SearchResponse<Person> response3 = client.search(s -> s.query(q -> q.bool(b -> b
.must(doeContainsQuery))), Person.class);
response3.hits().hits().forEach(hit -> log.info("Response 3: {}", hit.source()));
Query fullNameQuery = MatchQuery.of(m -> m.field("fullName")
.query("John"))
._toQuery();
SearchResponse<Person> response2 = client.search(s -> s.query(q -> q.bool(b -> b.must(fullNameQuery))), Person.class);
response2.hits()
.hits()
.forEach(hit -> log.info("Response 2: {}", hit.source()));
Query doeContainsQuery = SimpleQueryStringQuery.of(q -> q.query("*Doe"))
._toQuery();
SearchResponse<Person> response3 = client.search(s -> s.query(q -> q.bool(b -> b.must(doeContainsQuery))), Person.class);
response3.hits()
.hits()
.forEach(hit -> log.info("Response 3: {}", hit.source()));
Query simpleStringQuery = SimpleQueryStringQuery.of(q -> q.query("+John -Doe OR Janette"))._toQuery();
SearchResponse<Person> response4 = client.search(s -> s.query(q -> q.bool(b -> b
.must(simpleStringQuery))), Person.class);
response4.hits().hits().forEach(hit -> log.info("Response 4: {}", hit.source()));
Query simpleStringQuery = SimpleQueryStringQuery.of(q -> q.query("+John -Doe OR Janette"))
._toQuery();
SearchResponse<Person> response4 = client.search(s -> s.query(q -> q.bool(b -> b.must(simpleStringQuery))), Person.class);
response4.hits()
.hits()
.forEach(hit -> log.info("Response 4: {}", hit.source()));
SearchResponse<Person> response5 = client.search(s -> s.query(q -> q.bool(b -> b
.must(ageQuery)
.must(fullNameQuery)
.must(simpleStringQuery))), Person.class);
response5.hits().hits().forEach(hit -> log.info("Response 5: {}", hit.source()));
SearchResponse<Person> response5 = client.search(s -> s.query(q -> q.bool(b -> b.must(ageQuery)
.must(fullNameQuery)
.must(simpleStringQuery))), Person.class);
response5.hits()
.hits()
.forEach(hit -> log.info("Response 5: {}", hit.source()));
}
}