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

View File

@ -8,77 +8,56 @@
<parent> <parent>
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
<artifactId>parent-spring-5</artifactId> <artifactId>parent-boot-3</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-spring-5</relativePath> <relativePath>../../parent-boot-3</relativePath>
</parent> </parent>
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId> <artifactId>spring-data-elasticsearch</artifactId>
<version>${spring-data-elasticsearch.version}</version> <version>${spring-data-elasticsearch.version}</version>
</dependency> </dependency>
<dependency> <!--These two dependencies are inherited from spring-data-elasticsearch. Overriding to the newer version
<groupId>org.elasticsearch</groupId> will break tests. -->
<artifactId>elasticsearch</artifactId> <!-- <dependency>-->
<version>${elasticsearch.version}</version> <!-- <groupId>co.elastic.clients</groupId>-->
</dependency> <!-- <artifactId>elasticsearch-java</artifactId>-->
<dependency> <!-- <version>${elasticsearch.version}</version>-->
<groupId>com.alibaba</groupId> <!-- </dependency>-->
<artifactId>fastjson</artifactId> <!-- <dependency>-->
<version>${fastjson.version}</version> <!-- <groupId>com.fasterxml.jackson.core</groupId>-->
</dependency> <!-- <artifactId>jackson-databind</artifactId>-->
<!-- <version>${jackson.version}</version>-->
<!-- </dependency>-->
<!-- Here for backward compatibility-->
<dependency> <dependency>
<groupId>org.elasticsearch.client</groupId> <groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId> <artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.17.11</version> <version>7.17.11</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<version>1.18.28</version> <version>1.18.28</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.locationtech.spatial4j</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spatial4j</artifactId> <artifactId>spring-boot-autoconfigure</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>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<properties> <properties>
<spring-data-elasticsearch.version>5.1.2</spring-data-elasticsearch.version> <spring-data-elasticsearch.version>5.1.2</spring-data-elasticsearch.version>
<elasticsearch.version>8.9.0</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> <jackson.version>2.15.2</jackson.version>
</properties> </properties>

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -12,8 +12,6 @@ import java.util.Date;
public class Person { public class Person {
private int age; private int age;
private String fullName; private String fullName;
private Date dateOfBirth; private Date dateOfBirth;
} }

View File

@ -15,18 +15,18 @@ import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport; import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.junit.Before; import org.junit.jupiter.api.BeforeEach;
import org.junit.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Disabled;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.Date; import java.util.Date;
import java.util.List; 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. * This Manual test requires: Elasticsearch instance running on localhost:9200.
@ -35,30 +35,25 @@ 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 * 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 @Slf4j
@Disabled("Manual test")
public class ElasticSearchManualTest { public class ElasticSearchManualTest {
private ElasticsearchClient client = null; private ElasticsearchClient client = null;
@Before @BeforeEach
public void setUp() throws IOException { public void setUp() throws IOException {
RestClient restClient = RestClient RestClient restClient = RestClient.builder(HttpHost.create("http://localhost:9200"))
.builder(HttpHost.create("http://localhost:9200"))
.build(); .build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
client = new ElasticsearchClient(transport); client = new ElasticsearchClient(transport);
Person person1 = new Person(10, "John Doe", new Date()); Person person1 = new Person(10, "John Doe", new Date());
Person person2 = new Person(25, "Janette Doe", new Date()); Person person2 = new Person(25, "Janette Doe", new Date());
Person person3 = new Person(8, "Mark Doe", new Date()); Person person3 = new Person(8, "Mark Doe", new Date());
client.index(builder -> builder client.index(builder -> builder.index("person")
.index("person")
.id(person1.getFullName()) .id(person1.getFullName())
.document(person1)); .document(person1));
client.index(builder -> builder client.index(builder -> builder.index("person")
.index("person")
.id(person2.getFullName()) .id(person2.getFullName())
.document(person2)); .document(person2));
client.index(builder -> builder client.index(builder -> builder.index("person")
.index("person")
.id(person3.getFullName()) .id(person3.getFullName())
.document(person3)); .document(person3));
} }
@ -66,8 +61,7 @@ public class ElasticSearchManualTest {
@Test @Test
public void givenJsonDocument_whenJavaObject_thenIndexDocument() throws Exception { public void givenJsonDocument_whenJavaObject_thenIndexDocument() throws Exception {
Person person = new Person(20, "Mark Doe", new Date(1471466076564L)); Person person = new Person(20, "Mark Doe", new Date(1471466076564L));
IndexResponse response = client.index(i -> i IndexResponse response = client.index(i -> i.index("person")
.index("person")
.id(person.getFullName()) .id(person.getFullName())
.document(person)); .document(person));
@ -81,8 +75,7 @@ public class ElasticSearchManualTest {
public void givenJsonString_whenJavaObject_thenIndexDocument() throws Exception { public void givenJsonString_whenJavaObject_thenIndexDocument() throws Exception {
String jsonString = "{\"age\":10,\"dateOfBirth\":1471466076564,\"fullName\":\"John Doe\"}"; String jsonString = "{\"age\":10,\"dateOfBirth\":1471466076564,\"fullName\":\"John Doe\"}";
StringReader stringReader = new StringReader(jsonString); StringReader stringReader = new StringReader(jsonString);
IndexResponse response = client.index(i -> i IndexResponse response = client.index(i -> i.index("person")
.index("person")
.id("John Doe") .id("John Doe")
.withJson(stringReader)); .withJson(stringReader));
log.info("Indexed with version: {}", response.version()); log.info("Indexed with version: {}", response.version());
@ -93,8 +86,7 @@ public class ElasticSearchManualTest {
@Test @Test
public void givenDocumentId_whenJavaObject_thenDeleteDocument() throws Exception { public void givenDocumentId_whenJavaObject_thenDeleteDocument() throws Exception {
String documentId = "Mark Doe"; String documentId = "Mark Doe";
DeleteResponse response = client.delete(i -> i DeleteResponse response = client.delete(i -> i.index("person")
.index("person")
.id(documentId)); .id(documentId));
assertEquals(Result.Deleted, response.result()); assertEquals(Result.Deleted, response.result());
assertEquals("Mark Doe", response.id()); assertEquals("Mark Doe", response.id());
@ -103,23 +95,22 @@ public class ElasticSearchManualTest {
@Test @Test
public void givenSearchRequest_whenMatch_thenReturnAllResults() throws Exception { public void givenSearchRequest_whenMatch_thenReturnAllResults() throws Exception {
String searchText = "John"; String searchText = "John";
SearchResponse<Person> searchResponse = client.search(s -> s SearchResponse<Person> searchResponse = client.search(s -> s.index("person")
.index("person") .query(q -> q.match(t -> t.field("fullName")
.query(q -> q
.match(t -> t
.field("fullName")
.query(searchText))), Person.class); .query(searchText))), Person.class);
List<Hit<Person>> hits = searchResponse.hits().hits(); List<Hit<Person>> hits = searchResponse.hits()
.hits();
assertEquals(1, hits.size()); assertEquals(1, hits.size());
assertEquals("John Doe", hits.get(0).source().getFullName()); assertEquals("John Doe", hits.get(0)
.source()
.getFullName());
} }
@Test @Test
public void givenGetRequest_whenMatch_thenReturnAllResults() throws IOException { public void givenGetRequest_whenMatch_thenReturnAllResults() throws IOException {
String documentId = "John Doe"; String documentId = "John Doe";
GetResponse<Person> getResponse = client.get(s -> s GetResponse<Person> getResponse = client.get(s -> s.index("person")
.index("person")
.id(documentId), Person.class); .id(documentId), Person.class);
Person source = getResponse.source(); Person source = getResponse.source();
assertEquals("John Doe", source.getFullName()); assertEquals("John Doe", source.getFullName());
@ -128,47 +119,58 @@ public class ElasticSearchManualTest {
@Test @Test
public void givenSearchRequest_whenMatchAndRange_thenReturnAllResults() throws Exception { public void givenSearchRequest_whenMatchAndRange_thenReturnAllResults() throws Exception {
String searchText = "John"; String searchText = "John";
SearchResponse<Person> searchResponse = client.search(s -> s SearchResponse<Person> searchResponse = client.search(s -> s.index("person")
.index("person") .query(q -> q.match(t -> t.field("fullName")
.query(q -> q .query(searchText)))
.match(t -> t .query(q -> q.range(range -> range.field("age")
.field("fullName").query(searchText))) .from("1")
.query(q -> q .to("10"))), Person.class);
.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(1, hits.size());
assertEquals("John Doe", hits.get(0).source().getFullName()); assertEquals("John Doe", hits.get(0)
.source()
.getFullName());
} }
@Test @Test
public void givenMultipleQueries_thenReturnResults() throws Exception { public void givenMultipleQueries_thenReturnResults() throws Exception {
Query ageQuery = RangeQuery.of(r -> r.field("age").from("5").to("15"))._toQuery(); Query ageQuery = RangeQuery.of(r -> r.field("age")
SearchResponse<Person> response1 = client.search(s -> s.query(q -> q.bool(b -> b .from("5")
.must(ageQuery))), Person.class); .to("15"))
response1.hits().hits().forEach(hit -> log.info("Response 1: {}", hit.source())); ._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(); Query fullNameQuery = MatchQuery.of(m -> m.field("fullName")
SearchResponse<Person> response2 = client.search(s -> s.query(q -> q.bool(b -> b .query("John"))
.must(fullNameQuery))), Person.class); ._toQuery();
response2.hits().hits().forEach(hit -> log.info("Response 2: {}", hit.source())); SearchResponse<Person> response2 = client.search(s -> s.query(q -> q.bool(b -> b.must(fullNameQuery))), Person.class);
Query doeContainsQuery = SimpleQueryStringQuery.of(q -> q.query("*Doe"))._toQuery(); response2.hits()
SearchResponse<Person> response3 = client.search(s -> s.query(q -> q.bool(b -> b .hits()
.must(doeContainsQuery))), Person.class); .forEach(hit -> log.info("Response 2: {}", hit.source()));
response3.hits().hits().forEach(hit -> log.info("Response 3: {}", 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(); Query simpleStringQuery = SimpleQueryStringQuery.of(q -> q.query("+John -Doe OR Janette"))
SearchResponse<Person> response4 = client.search(s -> s.query(q -> q.bool(b -> b ._toQuery();
.must(simpleStringQuery))), Person.class); 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())); response4.hits()
.hits()
.forEach(hit -> log.info("Response 4: {}", hit.source()));
SearchResponse<Person> response5 = client.search(s -> s.query(q -> q.bool(b -> b SearchResponse<Person> response5 = client.search(s -> s.query(q -> q.bool(b -> b.must(ageQuery)
.must(ageQuery)
.must(fullNameQuery) .must(fullNameQuery)
.must(simpleStringQuery))), Person.class); .must(simpleStringQuery))), Person.class);
response5.hits().hits().forEach(hit -> log.info("Response 5: {}", hit.source())); response5.hits()
.hits()
.forEach(hit -> log.info("Response 5: {}", hit.source()));
} }
} }

View File

@ -1,199 +1,198 @@
package com.baeldung.elasticsearch; package com.baeldung.elasticsearch;
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.GeoShapeRelation;
import co.elastic.clients.elasticsearch.core.IndexResponse;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.io.StringReader;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import com.baeldung.spring.data.es.config.Config; import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; import org.apache.http.HttpHost;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; import org.elasticsearch.client.RestClient;
import org.elasticsearch.action.index.IndexRequest; import org.junit.jupiter.api.AfterEach;
import org.elasticsearch.action.index.IndexResponse; import org.junit.jupiter.api.BeforeEach;
import org.elasticsearch.action.search.SearchRequest; import org.junit.jupiter.api.Test;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.xcontent.XContentType;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.locationtech.jts.geom.Coordinate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/** /**
* This Manual test requires: Elasticsearch instance running on localhost:9200. * This Manual test requires: Elasticsearch instance running on localhost:9200.
* * <p>
* The following docker command can be used: docker run -d --name es762 -p * The following docker command can be used: docker run -d --name elastic-test -p 9200:9200 -e
* 9200:9200 -e "discovery.type=single-node" elasticsearch:7.6.2 * "discovery.type=single-node" -e "xpack.security.enabled=false"
* docker.elastic.co/elasticsearch/elasticsearch:8.9.0
*/ */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Config.class) @Slf4j
public class GeoQueriesManualTest { class GeoQueriesManualTest {
private static final String WONDERS_OF_WORLD = "wonders-of-world"; private static final String WONDERS_OF_WORLD = "wonders-of-world";
@Autowired private ElasticsearchClient client;
private RestHighLevelClient client;
@Before @BeforeEach
public void setUp() throws Exception { public void setUp() throws Exception {
String jsonObject = "{\"properties\":{\"name\":{\"type\":\"text\",\"index\":false},\"region\":{\"type\":\"geo_shape\"},\"location\":{\"type\":\"geo_point\"}}}"; RestClient restClient = RestClient.builder(HttpHost.create("http://localhost:9200"))
.build();
CreateIndexRequest req = new CreateIndexRequest(WONDERS_OF_WORLD); ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
req.mapping(jsonObject, XContentType.JSON); client = new ElasticsearchClient(transport);
log.info("Creating index: {}", WONDERS_OF_WORLD);
client.indices() client.indices()
.create(req, RequestOptions.DEFAULT); .create(builder -> builder.index(WONDERS_OF_WORLD)
} .mappings(typeMapping -> typeMapping.properties("region", region -> region.geoShape(gs -> gs))
.properties("location", location -> location.geoPoint(gp -> gp))));
// @Test
// public void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() throws IOException {
// String jsonObject = "{\"name\":\"Agra\",\"region\":{\"type\":\"envelope\",\"coordinates\":[[75,30.2],[80.1, 25]]}}";
// IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD);
// indexRequest.source(jsonObject, XContentType.JSON);
// IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
//
// String tajMahalId = response.getId();
//
// RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD);
// client.indices()
// .refresh(refreshRequest, RequestOptions.DEFAULT);
//
// Coordinate topLeft = new Coordinate(74, 31.2);
// Coordinate bottomRight = new Coordinate(81.1, 24);
//
// GeoShapeQueryBuilder qb = QueryBuilders.geoShapeQuery("region", new EnvelopeBuilder(topLeft, bottomRight).buildGeometry());
// qb.relation(ShapeRelation.INTERSECTS);
//
// SearchSourceBuilder source = new SearchSourceBuilder().query(qb);
// SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD);
// searchRequest.source(source);
//
// SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//
// List<String> ids = Arrays.stream(searchResponse.getHits()
// .getHits())
// .map(SearchHit::getId)
// .collect(Collectors.toList());
//
// assertTrue(ids.contains(tajMahalId));
// }
@Test
public void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() throws Exception {
String jsonObject = "{\"name\":\"Pyramids of Giza\",\"location\":[31.131302,29.976480]}";
IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD);
indexRequest.source(jsonObject, XContentType.JSON);
IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
String pyramidsOfGizaId = response.getId();
RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD);
client.indices()
.refresh(refreshRequest, RequestOptions.DEFAULT);
QueryBuilder qb = QueryBuilders.geoBoundingBoxQuery("location")
.setCorners(31, 30, 28, 32);
SearchSourceBuilder source = new SearchSourceBuilder().query(qb);
SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD);
searchRequest.source(source);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
List<String> ids = Arrays.stream(searchResponse.getHits()
.getHits())
.map(SearchHit::getId)
.collect(Collectors.toList());
assertTrue(ids.contains(pyramidsOfGizaId));
} }
@Test @Test
public void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() throws Exception { void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() throws IOException {
String jsonObject = "{\"name\":\"Lighthouse of alexandria\",\"location\":[31.131302,29.976480]}"; String jsonObject = """
{
"name":"Agra",
"region":{
"type":"envelope",
"coordinates":[[75,30.2],[80.1,25]]
}
}
""";
IndexResponse response = client.index(idx -> idx.index(WONDERS_OF_WORLD)
.withJson(new StringReader(jsonObject)));
IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD); String tajMahalId = response.id();
indexRequest.source(jsonObject, XContentType.JSON);
IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
String lighthouseOfAlexandriaId = response.getId();
RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD);
client.indices() client.indices()
.refresh(refreshRequest, RequestOptions.DEFAULT); .refresh();
QueryBuilder qb = QueryBuilders.geoDistanceQuery("location") StringReader jsonData = new StringReader("""
.point(29.976, 31.131) {
.distance(10, DistanceUnit.MILES); "type":"envelope",
"coordinates": [[74.0, 31.2], [81.1, 24.0 ] ]
}
""");
SearchSourceBuilder source = new SearchSourceBuilder().query(qb); SearchRequest searchRequest = new SearchRequest.Builder().query(query -> query.bool(boolQuery -> boolQuery.filter(query1 -> query1.geoShape(geoShapeQuery -> geoShapeQuery.field("region")
SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD); .shape(geoShapeFieldQuery -> geoShapeFieldQuery.relation(GeoShapeRelation.Within)
searchRequest.source(source); .shape(JsonData.from(jsonData)))))))
.build();
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); log.info("Search request: {}", searchRequest);
SearchResponse<Object> search = client.search(searchRequest, Object.class);
List<String> ids = Arrays.stream(searchResponse.getHits() log.info("Search response: {}", search);
.getHits()) List<String> searchResults = search.hits()
.map(SearchHit::getId) .hits()
.collect(Collectors.toList()); .stream()
assertTrue(ids.contains(lighthouseOfAlexandriaId)); .map(Hit::id)
.toList();
assertTrue(searchResults.contains(tajMahalId));
} }
@Test @Test
public void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() throws Exception { void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() throws Exception {
String jsonObject = "{\"name\":\"The Great Rann of Kutch\",\"location\":[69.859741,23.733732]}"; Location pyramidsOfGiza = new Location("Pyramids of Giza", List.of(31.1328, 29.9761));
IndexResponse response = client.index(builder -> builder.index(WONDERS_OF_WORLD)
.document(pyramidsOfGiza));
IndexRequest indexRequest = new IndexRequest(WONDERS_OF_WORLD); String pyramidsOfGizaId = response.id();
indexRequest.source(jsonObject, XContentType.JSON);
IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
String greatRannOfKutchid = response.getId(); log.info("Indexed pyramid of Giza: {}", pyramidsOfGizaId);
RefreshRequest refreshRequest = new RefreshRequest(WONDERS_OF_WORLD);
client.indices() client.indices()
.refresh(refreshRequest, RequestOptions.DEFAULT); .refresh();
List<GeoPoint> allPoints = new ArrayList<GeoPoint>(); SearchRequest.Builder builder = new SearchRequest.Builder().index(WONDERS_OF_WORLD);
allPoints.add(new GeoPoint(22.733, 68.859)); builder.query(query -> query.geoBoundingBox(geoBoundingBoxQuery -> geoBoundingBoxQuery.field("location")
allPoints.add(new GeoPoint(24.733, 68.859)); .boundingBox(geoBounds -> geoBounds.tlbr(bl4 -> bl4.topLeft(geoLocation -> geoLocation.coords(List.of(30.0, 31.0)))
allPoints.add(new GeoPoint(23, 70.859)); .bottomRight(geoLocation -> geoLocation.coords(List.of(32.0, 28.0)))))));
QueryBuilder qb = QueryBuilders.geoPolygonQuery("location", allPoints); SearchRequest build = builder.build();
log.info("Search request: {}", build);
SearchSourceBuilder source = new SearchSourceBuilder().query(qb); SearchResponse<Location> searchResponse = client.search(build, Location.class);
SearchRequest searchRequest = new SearchRequest(WONDERS_OF_WORLD); log.info("Search response: {}", searchResponse);
searchRequest.source(source); List<Location> returnedLocations = searchResponse.hits()
.hits()
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); .stream()
.map(Hit::source)
List<String> ids = Arrays.stream(searchResponse.getHits() .toList();
.getHits()) assertEquals(pyramidsOfGiza, returnedLocations.get(0));
.map(SearchHit::getId)
.collect(Collectors.toList());
assertTrue(ids.contains(greatRannOfKutchid));
} }
@After @Test
void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() throws Exception {
String jsonObject = """
{
"name":"Lighthouse of alexandria",
"location":{ "lat": 31.2139, "lon": 29.8856 }
}
""";
IndexResponse response = client.index(idx -> idx.index(WONDERS_OF_WORLD)
.withJson(new StringReader(jsonObject)));
String lightHouseOfAlexandriaId = response.id();
client.indices()
.refresh();
SearchRequest searchRequest = new SearchRequest.Builder().index(WONDERS_OF_WORLD)
.query(query -> query.geoDistance(geoDistanceQuery -> geoDistanceQuery.field("location")
.distance("10 miles")
.location(geoLocation -> geoLocation.latlon(latLonGeoLocation -> latLonGeoLocation.lon(29.88)
.lat(31.21)))))
.build();
log.info("Search request: {}", searchRequest);
SearchResponse<Object> search = client.search(searchRequest, Object.class);
log.info("Search response: {}", search);
List<String> ids = search.hits()
.hits()
.stream()
.map(Hit::id)
.toList();
assertTrue(ids.contains(lightHouseOfAlexandriaId));
}
@Test
void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() throws Exception {
String jsonObject = """
{
"name":"The Great Rann polygonPoints Kutch",
"location":{"lon": 69.859741, "lat": 23.733732}
}
""";
IndexResponse response = client.index(idx -> idx.index(WONDERS_OF_WORLD)
.withJson(new StringReader(jsonObject)));
String greatRannOfKutchid = response.id();
client.indices()
.refresh();
log.info("Indexed greatRannOfKutchid: {}", greatRannOfKutchid);
JsonData jsonData = JsonData.fromJson("""
{
"type":"polygon",
"coordinates":[[[68.859,22.733],[68.859,24.733],[70.859,23]]]
}
""");
SearchRequest build = new SearchRequest.Builder().query(query -> query.bool(boolQuery -> boolQuery.filter(query1 -> query1.geoShape(geoShapeQuery -> geoShapeQuery.field("location")
.shape(geoShapeFieldQuery -> geoShapeFieldQuery.relation(GeoShapeRelation.Within)
.shape(jsonData))))))
.build();
log.info("Search request: {}", build);
SearchResponse<Object> search = client.search(build, Object.class);
log.info("Search response: {}", search);
List<String> searchResults = search.hits()
.hits()
.stream()
.map(Hit::id)
.toList();
assertTrue(searchResults.contains(greatRannOfKutchid));
}
@AfterEach
public void destroy() throws Exception { public void destroy() throws Exception {
DeleteIndexRequest deleteIndex = new DeleteIndexRequest(WONDERS_OF_WORLD);
client.indices() client.indices()
.delete(deleteIndex, RequestOptions.DEFAULT); .delete(builder -> builder.index(WONDERS_OF_WORLD));
} }
} }