From 06741bf1feebd21c7f66ac91a6d6fe80cec218e6 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 11:57:43 +0000 Subject: [PATCH 01/11] feat : upgrade to spring boot 3.4.0 --- boot-mongodb-elasticsearch/pom.xml | 6 +- .../config/DataStoreConfiguration.java | 2 +- .../example/mongoes/config/Initializer.java | 6 +- .../example/mongoes/config/SwaggerConfig.java | 4 +- .../example/mongoes/config/WebFluxConfig.java | 11 +- .../com/example/mongoes/document/Address.java | 2 - .../CustomRestaurantESRepositoryImpl.java | 194 +++++++++--------- .../web/model/RestaurantRequestTest.java | 46 +++++ 8 files changed, 164 insertions(+), 107 deletions(-) create mode 100644 boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/model/RestaurantRequestTest.java diff --git a/boot-mongodb-elasticsearch/pom.xml b/boot-mongodb-elasticsearch/pom.xml index 86191b5eb..8d8304073 100644 --- a/boot-mongodb-elasticsearch/pom.xml +++ b/boot-mongodb-elasticsearch/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.6 + 3.4.0 com.example.mongoes @@ -19,7 +19,7 @@ UTF-8 21 - 2.6.0 + 2.7.0 ${project.build.directory}/test-results 2.43.0 @@ -229,7 +229,7 @@ - 1.23.0 + 1.25.0 diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/DataStoreConfiguration.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/DataStoreConfiguration.java index cfb12a5d8..d66c29e90 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/DataStoreConfiguration.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/DataStoreConfiguration.java @@ -25,7 +25,7 @@ @ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, value = ReactiveElasticsearchRepository.class)) -public class DataStoreConfiguration { +class DataStoreConfiguration { @Bean ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory factory) { diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/Initializer.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/Initializer.java index e2239596b..bc3b9e417 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/Initializer.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/Initializer.java @@ -3,7 +3,6 @@ import com.example.mongoes.utils.AppConstants; import com.example.mongoes.web.service.RestaurantService; import java.io.IOException; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.context.event.ApplicationStartedEvent; @@ -12,13 +11,16 @@ import org.springframework.stereotype.Component; @Component -@RequiredArgsConstructor @Slf4j @Profile(AppConstants.PROFILE_NOT_TEST) public class Initializer implements CommandLineRunner { private final RestaurantService restaurantService; + public Initializer(RestaurantService restaurantService) { + this.restaurantService = restaurantService; + } + @Override public void run(String... args) throws IOException { log.info("Running Initializer....."); diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/SwaggerConfig.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/SwaggerConfig.java index 90333d15e..9c2740590 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/SwaggerConfig.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/SwaggerConfig.java @@ -7,9 +7,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -@Configuration +@Configuration(proxyBeanMethods = false) @OpenAPIDefinition(info = @Info(title = "spring-boot-mongodb-elasticsearch", version = "v1")) -public class SwaggerConfig { +class SwaggerConfig { @Bean OpenAPI openAPI() { diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/WebFluxConfig.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/WebFluxConfig.java index af97f6cd3..3ba08ee31 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/WebFluxConfig.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/WebFluxConfig.java @@ -1,16 +1,21 @@ package com.example.mongoes.config; -import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.web.config.SpringDataJacksonConfiguration; import org.springframework.web.reactive.config.CorsRegistry; import org.springframework.web.reactive.config.WebFluxConfigurer; -@Configuration -@RequiredArgsConstructor +@Configuration(proxyBeanMethods = false) +@Import(SpringDataJacksonConfiguration.class) public class WebFluxConfig implements WebFluxConfigurer { private final ApplicationProperties properties; + public WebFluxConfig(ApplicationProperties properties) { + this.properties = properties; + } + @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(properties.getCors().getPathPattern()) diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Address.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Address.java index 2fb4cb54f..b9a102821 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Address.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Address.java @@ -1,6 +1,5 @@ package com.example.mongoes.document; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -29,7 +28,6 @@ public class Address { * * */ - @JsonDeserialize(as = Point.class) @GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) @GeoPointField @org.springframework.data.elasticsearch.annotations.Field("coord") diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/elasticsearch/repository/CustomRestaurantESRepositoryImpl.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/elasticsearch/repository/CustomRestaurantESRepositoryImpl.java index 9f88494c6..dbeebc086 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/elasticsearch/repository/CustomRestaurantESRepositoryImpl.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/elasticsearch/repository/CustomRestaurantESRepositoryImpl.java @@ -7,11 +7,9 @@ import co.elastic.clients.elasticsearch._types.query_dsl.Operator; import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; import co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType; -import co.elastic.clients.json.JsonData; import com.example.mongoes.document.Restaurant; import java.util.ArrayList; import java.util.List; -import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -27,7 +25,6 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -@RequiredArgsConstructor public class CustomRestaurantESRepositoryImpl implements CustomRestaurantESRepository { private static final String BOROUGH = "borough"; @@ -35,6 +32,11 @@ public class CustomRestaurantESRepositoryImpl implements CustomRestaurantESRepos private static final int PAGE_SIZE = 1_000; private final ReactiveElasticsearchOperations reactiveElasticsearchOperations; + public CustomRestaurantESRepositoryImpl( + ReactiveElasticsearchOperations reactiveElasticsearchOperations) { + this.reactiveElasticsearchOperations = reactiveElasticsearchOperations; + } + @Override public Flux> searchWithin( GeoPoint geoPoint, Double distance, String unit) { @@ -232,10 +234,15 @@ public Mono> searchRestaurantIdRange( .withQuery( QueryBuilders.range( rangeBuilder -> - rangeBuilder - .field("restaurant_id") - .lte(JsonData.of(upperLimit)) - .gte(JsonData.of(lowerLimit)))) + rangeBuilder.term( + builder -> + builder.field("restaurant_id") + .lte( + String.valueOf( + upperLimit)) + .gte( + String.valueOf( + lowerLimit))))) .build(); query.setPageable(pageable); @@ -250,102 +257,101 @@ public Mono> searchDateRange( .withQuery( QueryBuilders.range( rangeBuilder -> - rangeBuilder - .field("grades.date") - .lte(JsonData.of(toDate)) - .gte(JsonData.of(fromDate)))) + rangeBuilder.date( + builder -> + builder.field("grades.date") + .lte(toDate) + .gte(fromDate)))) .build(); query.setPageable(pageable); return reactiveElasticsearchOperations.searchForPage(query, Restaurant.class); } - /** + * below is the console query * - * below is the console query {@snippet : - - """ POST /restaurant/_search?size=15&pretty - { - "query": { - "multi_match": { - "query": "Pizza", - "fields": [ - "restautant_name", - "borough", - "cuisine" - ], - "operator": "or" - } - }, - "aggs": { - "MyCuisine": { - "terms": { - "field": "cuisine", "size": 1000, "order": { - "_count": "desc" - } - } - }, - "MyBorough": { - "terms": { - "field": "borough" , "size": 1000 - } - }, - "MyDateRange": { - "date_range": { - "field": "grades.date", - "format": "dd-MM-yyy'T'hh:mm:ss", - "ranges": [ - { - "key": "Older", - "to": "now-12y-1d/y" - }, - { - "from": "now-12y/y", - "to": "now-11y/y" - }, - { - "from": "now-11y/y", - "to": "now-10y/y" - }, - { - "from": "now-10y/y", - "to": "now-9y/y" - }, - { - "from": "now-9y/y", - "to": "now-8y/y" - }, - { - "from": "now-8y/y", - "to": "now-7y/y" - }, - { - "from": "now-7y/y", - "to": "now-6y/y" - }, - { - "from": "now-6y/y", - "to": "now-5y/y" - }, - { - "from": "now-5y/y", - "to": "now-4y/y" - }, - { - "key": "Newer", - "from": "now-0y/y", - "to": "now/d" - } - ] - } - } - } - } - """; - } - * + * {@snippet : * + * """ POST /restaurant/_search?size=15&pretty + * { + * "query": { + * "multi_match": { + * "query": "Pizza", + * "fields": [ + * "restautant_name", + * "borough", + * "cuisine" + * ], + * "operator": "or" + * } + * }, + * "aggs": { + * "MyCuisine": { + * "terms": { + * "field": "cuisine", "size": 1000, "order": { + * "_count": "desc" + * } + * } + * }, + * "MyBorough": { + * "terms": { + * "field": "borough" , "size": 1000 + * } + * }, + * "MyDateRange": { + * "date_range": { + * "field": "grades.date", + * "format": "dd-MM-yyy'T'hh:mm:ss", + * "ranges": [ + * { + * "key": "Older", + * "to": "now-12y-1d/y" + * }, + * { + * "from": "now-12y/y", + * "to": "now-11y/y" + * }, + * { + * "from": "now-11y/y", + * "to": "now-10y/y" + * }, + * { + * "from": "now-10y/y", + * "to": "now-9y/y" + * }, + * { + * "from": "now-9y/y", + * "to": "now-8y/y" + * }, + * { + * "from": "now-8y/y", + * "to": "now-7y/y" + * }, + * { + * "from": "now-7y/y", + * "to": "now-6y/y" + * }, + * { + * "from": "now-6y/y", + * "to": "now-5y/y" + * }, + * { + * "from": "now-5y/y", + * "to": "now-4y/y" + * }, + * { + * "key": "Newer", + * "from": "now-0y/y", + * "to": "now/d" + * } + * ] + * } + * } + * } + * } + * """; + * } */ @Override public Mono> aggregateSearch( diff --git a/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/model/RestaurantRequestTest.java b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/model/RestaurantRequestTest.java new file mode 100644 index 000000000..58d40c8b0 --- /dev/null +++ b/boot-mongodb-elasticsearch/src/test/java/com/example/mongoes/web/model/RestaurantRequestTest.java @@ -0,0 +1,46 @@ +package com.example.mongoes.web.model; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.example.mongoes.document.Address; +import com.example.mongoes.document.Grades; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.boot.test.json.JacksonTester; +import org.springframework.boot.test.json.JsonContent; +import org.springframework.context.annotation.Import; +import org.springframework.data.geo.Point; +import org.springframework.data.web.config.SpringDataJacksonConfiguration; + +@JsonTest +@Import(SpringDataJacksonConfiguration.class) +class RestaurantRequestTest { + + @Autowired private JacksonTester jacksonTester; + + @Test + void serializeInCorrectFormat() throws IOException { + Address address = new Address(); + address.setLocation(new Point(-73.9, 40.8)); + Grades grade = new Grades("A", LocalDateTime.of(2022, 1, 1, 1, 1, 1), 15); + Grades grade1 = new Grades("B", LocalDateTime.of(2022, 3, 31, 23, 59, 59), 15); + RestaurantRequest restaurantRequest = + new RestaurantRequest( + 1L, + "junitRestaurant", + "junitBorough", + "junitCuisine", + address, + List.of(grade, grade1)); + + JsonContent json = jacksonTester.write(restaurantRequest); + assertThat(json.getJson()).isNotNull(); + + RestaurantRequest orderRequest = jacksonTester.parseObject(json.getJson()); + assertThat(orderRequest).isNotNull(); + } +} From 0ad02e5c9da19cda7cfa6a0f19cf3343549742b0 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 12:28:28 +0000 Subject: [PATCH 02/11] feat : remove lombok --- boot-mongodb-elasticsearch/pom.xml | 12 +-- .../mongoes/config/ApplicationProperties.java | 51 ++++++++++- .../example/mongoes/config/Initializer.java | 5 +- .../com/example/mongoes/document/Address.java | 49 ++++++++-- .../mongoes/document/ChangeStreamResume.java | 20 ++++- .../com/example/mongoes/document/Grades.java | 48 ++++++++-- .../example/mongoes/document/Restaurant.java | 89 +++++++++++++++++-- .../CustomRestaurantESRepositoryImpl.java | 13 +-- ...ustomChangeStreamResumeRepositoryImpl.java | 6 +- .../example/mongoes/utils/DateUtility.java | 4 +- .../web/controller/RestaurantController.java | 6 +- .../web/controller/SearchController.java | 6 +- .../web/service/RestaurantService.java | 19 ++-- .../mongoes/web/service/SearchService.java | 8 +- 14 files changed, 275 insertions(+), 61 deletions(-) diff --git a/boot-mongodb-elasticsearch/pom.xml b/boot-mongodb-elasticsearch/pom.xml index 8d8304073..86ce2a611 100644 --- a/boot-mongodb-elasticsearch/pom.xml +++ b/boot-mongodb-elasticsearch/pom.xml @@ -53,11 +53,7 @@ org.springframework.boot spring-boot-starter-webflux - - org.glassfish.jaxb - jaxb-runtime - provided - + org.springframework.boot spring-boot-starter-data-mongodb-reactive @@ -78,11 +74,7 @@ spring-boot-configuration-processor true - - org.projectlombok - lombok - true - + org.springdoc diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/ApplicationProperties.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/ApplicationProperties.java index 508f35d24..45681657b 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/ApplicationProperties.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/ApplicationProperties.java @@ -1,19 +1,64 @@ package com.example.mongoes.config; -import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; -@Data @ConfigurationProperties("application") public class ApplicationProperties { private Cors cors = new Cors(); - @Data + public Cors getCors() { + return cors; + } + + public void setCors(Cors cors) { + this.cors = cors; + } + public static class Cors { private String pathPattern = "/api/**"; private String allowedMethods = "*"; private String allowedHeaders = "*"; private String allowedOriginPatterns = "*"; private boolean allowCredentials = true; + + public String getPathPattern() { + return pathPattern; + } + + public void setPathPattern(String pathPattern) { + this.pathPattern = pathPattern; + } + + public String getAllowedMethods() { + return allowedMethods; + } + + public void setAllowedMethods(String allowedMethods) { + this.allowedMethods = allowedMethods; + } + + public String getAllowedHeaders() { + return allowedHeaders; + } + + public void setAllowedHeaders(String allowedHeaders) { + this.allowedHeaders = allowedHeaders; + } + + public String getAllowedOriginPatterns() { + return allowedOriginPatterns; + } + + public void setAllowedOriginPatterns(String allowedOriginPatterns) { + this.allowedOriginPatterns = allowedOriginPatterns; + } + + public boolean isAllowCredentials() { + return allowCredentials; + } + + public void setAllowCredentials(boolean allowCredentials) { + this.allowCredentials = allowCredentials; + } } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/Initializer.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/Initializer.java index bc3b9e417..a02cd9708 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/Initializer.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/Initializer.java @@ -3,7 +3,8 @@ import com.example.mongoes.utils.AppConstants; import com.example.mongoes.web.service.RestaurantService; import java.io.IOException; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.annotation.Profile; @@ -11,10 +12,10 @@ import org.springframework.stereotype.Component; @Component -@Slf4j @Profile(AppConstants.PROFILE_NOT_TEST) public class Initializer implements CommandLineRunner { + private static final Logger log = LoggerFactory.getLogger(Initializer.class); private final RestaurantService restaurantService; public Initializer(RestaurantService restaurantService) { diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Address.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Address.java index b9a102821..d2e991859 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Address.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Address.java @@ -1,17 +1,12 @@ package com.example.mongoes.document; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import java.util.StringJoiner; import org.springframework.data.elasticsearch.annotations.GeoPointField; import org.springframework.data.geo.Point; import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; import org.springframework.data.mongodb.core.mapping.Field; -@Data -@AllArgsConstructor -@NoArgsConstructor public class Address { private String building; @@ -37,4 +32,46 @@ public class Address { private String street; private Integer zipcode; + + public String getBuilding() { + return building; + } + + public void setBuilding(String building) { + this.building = building; + } + + public Point getLocation() { + return location; + } + + public void setLocation(Point location) { + this.location = location; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public Integer getZipcode() { + return zipcode; + } + + public void setZipcode(Integer zipcode) { + this.zipcode = zipcode; + } + + @Override + public String toString() { + return new StringJoiner(", ", Address.class.getSimpleName() + "[", "]") + .add("building='" + building + "'") + .add("location=" + location) + .add("street='" + street + "'") + .add("zipcode=" + zipcode) + .toString(); + } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/ChangeStreamResume.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/ChangeStreamResume.java index 4cb847359..e58c4f508 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/ChangeStreamResume.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/ChangeStreamResume.java @@ -1,17 +1,29 @@ package com.example.mongoes.document; -import lombok.Getter; -import lombok.Setter; import org.bson.BsonTimestamp; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; @Document -@Getter -@Setter public class ChangeStreamResume { @Id private String id; private BsonTimestamp resumeTimestamp; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public BsonTimestamp getResumeTimestamp() { + return resumeTimestamp; + } + + public void setResumeTimestamp(BsonTimestamp resumeTimestamp) { + this.resumeTimestamp = resumeTimestamp; + } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Grades.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Grades.java index 8b102b7d5..283c5c3e3 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Grades.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Grades.java @@ -3,15 +3,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import java.time.LocalDateTime; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import java.util.StringJoiner; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; -@Data -@AllArgsConstructor -@NoArgsConstructor public class Grades { private String grade; @@ -23,4 +18,45 @@ public class Grades { private LocalDateTime date; private Integer score; + + public Grades() {} + + public Grades(String grade, LocalDateTime date, Integer score) { + this.grade = grade; + this.date = date; + this.score = score; + } + + public LocalDateTime getDate() { + return date; + } + + public void setDate(LocalDateTime date) { + this.date = date; + } + + public String getGrade() { + return grade; + } + + public void setGrade(String grade) { + this.grade = grade; + } + + public Integer getScore() { + return score; + } + + public void setScore(Integer score) { + this.score = score; + } + + @Override + public String toString() { + return new StringJoiner(", ", Grades.class.getSimpleName() + "[", "]") + .add("date=" + date) + .add("grade='" + grade + "'") + .add("score=" + score) + .toString(); + } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Restaurant.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Restaurant.java index f19f29450..64eeac843 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Restaurant.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/document/Restaurant.java @@ -4,10 +4,7 @@ import jakarta.validation.constraints.NotBlank; import java.util.ArrayList; import java.util.List; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; +import java.util.StringJoiner; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.annotation.Version; @@ -17,14 +14,10 @@ import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; -@Getter -@Setter @org.springframework.data.elasticsearch.annotations.Document( indexName = AppConstants.RESTAURANT_COLLECTION) @Document(collection = AppConstants.RESTAURANT_COLLECTION) -@NoArgsConstructor @TypeAlias(AppConstants.RESTAURANT_COLLECTION) -@ToString @Setting(shards = 3) public class Restaurant { @@ -53,4 +46,84 @@ public class Restaurant { private List grades = new ArrayList<>(); @Version private Long version; + + public Restaurant() {} + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Long getRestaurantId() { + return restaurantId; + } + + public void setRestaurantId(Long restaurantId) { + this.restaurantId = restaurantId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public String getBorough() { + return borough; + } + + public void setBorough(String borough) { + this.borough = borough; + } + + public String getCuisine() { + return cuisine; + } + + public void setCuisine(String cuisine) { + this.cuisine = cuisine; + } + + public List getGrades() { + return grades; + } + + public void setGrades(List grades) { + this.grades = grades; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } + + @Override + public String toString() { + return new StringJoiner(", ", Restaurant.class.getSimpleName() + "[", "]") + .add("id='" + id + "'") + .add("restaurantId=" + restaurantId) + .add("name='" + name + "'") + .add("address=" + address) + .add("borough='" + borough + "'") + .add("cuisine='" + cuisine + "'") + .add("grades=" + grades) + .add("version=" + version) + .toString(); + } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/elasticsearch/repository/CustomRestaurantESRepositoryImpl.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/elasticsearch/repository/CustomRestaurantESRepositoryImpl.java index dbeebc086..22f384c31 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/elasticsearch/repository/CustomRestaurantESRepositoryImpl.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/elasticsearch/repository/CustomRestaurantESRepositoryImpl.java @@ -272,14 +272,14 @@ public Mono> searchDateRange( * below is the console query * * {@snippet : - * - * """ POST /restaurant/_search?size=15&pretty + * """ + * POST /restaurant/_search?size=15&pretty * { * "query": { * "multi_match": { * "query": "Pizza", * "fields": [ - * "restautant_name", + * "restaurant_name", * "borough", * "cuisine" * ], @@ -289,14 +289,17 @@ public Mono> searchDateRange( * "aggs": { * "MyCuisine": { * "terms": { - * "field": "cuisine", "size": 1000, "order": { + * "field": "cuisine", + * "size": 1000, + * "order": { * "_count": "desc" * } * } * }, * "MyBorough": { * "terms": { - * "field": "borough" , "size": 1000 + * "field": "borough", + * "size": 1000 * } * }, * "MyDateRange": { diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/mongodb/repository/CustomChangeStreamResumeRepositoryImpl.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/mongodb/repository/CustomChangeStreamResumeRepositoryImpl.java index 16e66961a..466bd8cea 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/mongodb/repository/CustomChangeStreamResumeRepositoryImpl.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/mongodb/repository/CustomChangeStreamResumeRepositoryImpl.java @@ -2,20 +2,22 @@ import com.example.mongoes.document.ChangeStreamResume; import com.mongodb.client.result.UpdateResult; -import lombok.RequiredArgsConstructor; import org.bson.BsonTimestamp; import org.springframework.data.mongodb.core.ReactiveMongoTemplate; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import reactor.core.publisher.Mono; -@RequiredArgsConstructor public class CustomChangeStreamResumeRepositoryImpl implements CustomChangeStreamResumeRepository { private final ReactiveMongoTemplate reactiveMongoTemplate; private static final String FIELD_NAME = "resumeTimestamp"; + public CustomChangeStreamResumeRepositoryImpl(ReactiveMongoTemplate reactiveMongoTemplate) { + this.reactiveMongoTemplate = reactiveMongoTemplate; + } + @Override public Mono update(BsonTimestamp resumeTimestamp) { Query query = new Query(); diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/utils/DateUtility.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/utils/DateUtility.java index aea1bcaba..7d8216185 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/utils/DateUtility.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/utils/DateUtility.java @@ -3,11 +3,9 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; -import lombok.experimental.UtilityClass; -@UtilityClass public class DateUtility { - public LocalDateTime convertToLocalDateViaInstant(Date dateToConvert) { + public static LocalDateTime convertToLocalDateViaInstant(Date dateToConvert) { return dateToConvert.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java index e4d1ac0ac..40ccdc8bc 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java @@ -9,7 +9,6 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.Size; import java.net.URI; -import lombok.RequiredArgsConstructor; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -23,13 +22,16 @@ import reactor.core.publisher.Mono; @RestController -@RequiredArgsConstructor @Timed @RequestMapping("/api/restaurant") public class RestaurantController { private final RestaurantService restaurantService; + public RestaurantController(RestaurantService restaurantService) { + this.restaurantService = restaurantService; + } + @GetMapping public Mono>> findAllRestaurants( @Valid @RequestParam(defaultValue = "10") @Size(max = 999) int limit, diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java index f24d08f39..d9871708f 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java @@ -6,7 +6,6 @@ import com.example.mongoes.web.service.SearchService; import io.micrometer.core.annotation.Timed; import java.util.List; -import lombok.RequiredArgsConstructor; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -17,11 +16,14 @@ @RestController @Timed -@RequiredArgsConstructor public class SearchController { private final SearchService searchService; + public SearchController(SearchService searchService) { + this.searchService = searchService; + } + @GetMapping("/search/borough") public Mono>> searchPhrase( @RequestParam String query, diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java index d854b33a5..b6f64500d 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java @@ -20,10 +20,10 @@ import java.util.Date; import java.util.List; import java.util.Objects; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.bson.BsonTimestamp; import org.bson.Document; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.data.domain.PageRequest; @@ -40,16 +40,25 @@ import reactor.core.publisher.Mono; @Service -@Slf4j -@RequiredArgsConstructor public class RestaurantService { + private static final Logger log = LoggerFactory.getLogger(RestaurantService.class); private final RestaurantRepository restaurantRepository; private final RestaurantESRepository restaurantESRepository; private final ChangeStreamResumeRepository changeStreamResumeRepository; - private final ReactiveMongoTemplate reactiveMongoTemplate; + public RestaurantService( + RestaurantRepository restaurantRepository, + RestaurantESRepository restaurantESRepository, + ChangeStreamResumeRepository changeStreamResumeRepository, + ReactiveMongoTemplate reactiveMongoTemplate) { + this.restaurantRepository = restaurantRepository; + this.restaurantESRepository = restaurantESRepository; + this.changeStreamResumeRepository = changeStreamResumeRepository; + this.reactiveMongoTemplate = reactiveMongoTemplate; + } + public Flux loadData() throws IOException { Resource input = new ClassPathResource("restaurants.json"); Path path = input.getFile().toPath(); diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/SearchService.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/SearchService.java index cde6d37cf..e65779c04 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/SearchService.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/SearchService.java @@ -10,8 +10,6 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -import lombok.RequiredArgsConstructor; -import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -20,15 +18,19 @@ import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.data.elasticsearch.core.geo.GeoPoint; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Service -@RequiredArgsConstructor public class SearchService { private final RestaurantESRepository restaurantESRepository; + public SearchService(RestaurantESRepository restaurantESRepository) { + this.restaurantESRepository = restaurantESRepository; + } + public Mono> searchMatchBorough(String query, Integer offset, Integer limit) { Pageable pageable = PageRequest.of(offset, limit); return Mono.just(restaurantESRepository.findByBorough(query, pageable)); From eaec2b646b27bb75e608508e0d12d40e2f921f07 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 12:46:28 +0000 Subject: [PATCH 03/11] feat : polish Code --- .../web/controller/RestaurantController.java | 27 +++++----- .../web/controller/SearchController.java | 15 ++++-- .../web/service/AggregationProcessor.java | 49 +++++++++++++++++ .../mongoes/web/service/SearchService.java | 53 +++---------------- 4 files changed, 80 insertions(+), 64 deletions(-) create mode 100644 boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java index 40ccdc8bc..76a39deea 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java @@ -77,18 +77,19 @@ public Mono> addNotesToRestaurant( } @PostMapping - public ResponseEntity createRestaurant( - @RequestBody RestaurantRequest restaurantRequest) { - return ResponseEntity.created( - URI.create( - String.format( - "/restaurant/%s", - this.restaurantService - .createRestaurant(restaurantRequest) - .map(Restaurant::getName)))) - .body( - new GenericMessage( - "restaurant with name %s created" - .formatted(restaurantRequest.name()))); + public Mono> createRestaurant( + @RequestBody @Valid RestaurantRequest restaurantRequest) { + return this.restaurantService + .createRestaurant(restaurantRequest) + .map( + restaurant -> + ResponseEntity.created( + URI.create("/restaurant/" + restaurant.getName())) + .body( + new GenericMessage( + "restaurant with name %s created" + .formatted( + restaurantRequest + .name())))); } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java index d9871708f..0b4c0949f 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java @@ -5,6 +5,8 @@ import com.example.mongoes.response.ResultData; import com.example.mongoes.web.service.SearchService; import io.micrometer.core.annotation.Timed; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import java.util.List; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.http.ResponseEntity; @@ -145,12 +147,17 @@ public Mono> aggregateSearch( .map(ResponseEntity::ok); } + @Operation( + summary = "Search restaurants within range", + description = "Find restaurants within specified distance from given coordinates") @GetMapping("/search/restaurant/withInRange") public Flux searchRestaurantsWithInRange( - @RequestParam Double lat, - @RequestParam Double lon, - @RequestParam Double distance, - @RequestParam(defaultValue = "km", required = false) String unit) { + @Parameter(description = "Latitude coordinate") @RequestParam Double lat, + @Parameter(description = "Longitude coordinate") @RequestParam Double lon, + @Parameter(description = "Distance from coordinates") @RequestParam Double distance, + @Parameter(description = "Unit of distance (km/mi)", example = "km") + @RequestParam(defaultValue = "km", required = false) + String unit) { return this.searchService.searchRestaurantsWithInRange(lat, lon, distance, unit); } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java new file mode 100644 index 000000000..e0cbff7c2 --- /dev/null +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java @@ -0,0 +1,49 @@ +package com.example.mongoes.web.service; + +import co.elastic.clients.elasticsearch._types.aggregations.Aggregate; +import co.elastic.clients.elasticsearch._types.aggregations.RangeBucket; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregation; +import org.springframework.stereotype.Service; + +@Service +class AggregationProcessor { + + public Map> processAggregations( + Map aggregationMap) { + Map> resultMap = new HashMap<>(); + aggregationMap.forEach( + (String aggregateKey, ElasticsearchAggregation aggregation) -> { + Map countMap = new HashMap<>(); + Aggregate aggregate = aggregation.aggregation().getAggregate(); + if (aggregate.isSterms()) { + aggregate + .sterms() + .buckets() + .array() + .forEach( + stringTermsBucket -> + countMap.put( + stringTermsBucket.key().stringValue(), + stringTermsBucket.docCount())); + } else if (aggregate.isDateRange()) { + List bucketList = aggregate.dateRange().buckets().array(); + bucketList.forEach( + rangeBucket -> { + if (rangeBucket.docCount() != 0) { + countMap.put( + rangeBucket.fromAsString() + + " - " + + rangeBucket.toAsString(), + rangeBucket.docCount()); + } + }); + } + resultMap.put(aggregateKey, countMap); + }); + + return resultMap; + } +} diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/SearchService.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/SearchService.java index e65779c04..ac65c8e10 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/SearchService.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/SearchService.java @@ -1,7 +1,5 @@ package com.example.mongoes.web.service; -import co.elastic.clients.elasticsearch._types.aggregations.Aggregate; -import co.elastic.clients.elasticsearch._types.aggregations.RangeBucket; import com.example.mongoes.document.Restaurant; import com.example.mongoes.elasticsearch.repository.RestaurantESRepository; import com.example.mongoes.response.AggregationSearchResponse; @@ -9,11 +7,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregation; import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregations; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.data.elasticsearch.core.geo.GeoPoint; @@ -26,9 +22,13 @@ public class SearchService { private final RestaurantESRepository restaurantESRepository; + private final AggregationProcessor aggregationProcessor; - public SearchService(RestaurantESRepository restaurantESRepository) { + public SearchService( + RestaurantESRepository restaurantESRepository, + AggregationProcessor aggregationProcessor) { this.restaurantESRepository = restaurantESRepository; + this.aggregationProcessor = aggregationProcessor; } public Mono> searchMatchBorough(String query, Integer offset, Integer limit) { @@ -123,7 +123,7 @@ public Mono aggregateSearch( Map> map = new HashMap<>(); if (elasticsearchAggregations != null) { map = - aggregationFunction.apply( + aggregationProcessor.processAggregations( elasticsearchAggregations.aggregationsAsMap()); } return new AggregationSearchResponse( @@ -135,47 +135,6 @@ public Mono aggregateSearch( }); } - final Function, Map>> - aggregationFunction = - aggregationMap -> { - Map> resultMap = new HashMap<>(); - aggregationMap.forEach( - (String aggregateKey, ElasticsearchAggregation aggregation) -> { - Map countMap = new HashMap<>(); - Aggregate aggregate = aggregation.aggregation().getAggregate(); - if (aggregate.isSterms()) { - aggregate - .sterms() - .buckets() - .array() - .forEach( - stringTermsBucket -> - countMap.put( - stringTermsBucket - .key() - .stringValue(), - stringTermsBucket - .docCount())); - } else if (aggregate.isDateRange()) { - List bucketList = - aggregate.dateRange().buckets().array(); - bucketList.forEach( - rangeBucket -> { - if (rangeBucket.docCount() != 0) { - countMap.put( - rangeBucket.fromAsString() - + " - " - + rangeBucket.toAsString(), - rangeBucket.docCount()); - } - }); - } - resultMap.put(aggregateKey, countMap); - }); - - return resultMap; - }; - public Flux searchRestaurantsWithInRange( Double lat, Double lon, Double distance, String unit) { GeoPoint location = new GeoPoint(lat, lon); From 38841a046180730d32ebd0894098dd8476399af5 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 12:54:09 +0000 Subject: [PATCH 04/11] adds java doc --- .../main/java/com/example/mongoes/utils/DateUtility.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/utils/DateUtility.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/utils/DateUtility.java index f6341461d..ff36143f5 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/utils/DateUtility.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/utils/DateUtility.java @@ -5,6 +5,13 @@ import java.util.Date; public class DateUtility { + + /** + * Converts a {@link Date} to {@link LocalDateTime} using the system default timezone. + * + * @param dateToConvert the date to convert, can be null + * @return the converted {@link LocalDateTime} or null if input is null + */ public static LocalDateTime convertToLocalDateViaInstant(Date dateToConvert) { if (dateToConvert == null) { return null; From 125f5791e4901cc0f94eaddc8e7426981df39ebd Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 18:32:09 +0530 Subject: [PATCH 05/11] adds code review comments Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../web/service/AggregationProcessor.java | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java index e0cbff7c2..a3a021c0e 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java @@ -11,39 +11,51 @@ @Service class AggregationProcessor { + /** + * Processes Elasticsearch aggregations and returns a structured map of results. + * + * @param aggregationMap Map of aggregation key to ElasticsearchAggregation + * @return Map of aggregation key to counts, where counts is a map of bucket key to document count + * @throws IllegalArgumentException if aggregationMap is null + */ public Map> processAggregations( Map aggregationMap) { + if (aggregationMap == null) { + throw new IllegalArgumentException("aggregationMap must not be null"); + } Map> resultMap = new HashMap<>(); aggregationMap.forEach( (String aggregateKey, ElasticsearchAggregation aggregation) -> { Map countMap = new HashMap<>(); Aggregate aggregate = aggregation.aggregation().getAggregate(); - if (aggregate.isSterms()) { - aggregate - .sterms() - .buckets() - .array() - .forEach( - stringTermsBucket -> - countMap.put( - stringTermsBucket.key().stringValue(), - stringTermsBucket.docCount())); - } else if (aggregate.isDateRange()) { - List bucketList = aggregate.dateRange().buckets().array(); - bucketList.forEach( - rangeBucket -> { - if (rangeBucket.docCount() != 0) { - countMap.put( - rangeBucket.fromAsString() - + " - " - + rangeBucket.toAsString(), - rangeBucket.docCount()); - } - }); - } + processAggregate(aggregate, countMap); resultMap.put(aggregateKey, countMap); }); - return resultMap; } + + private void processAggregate(Aggregate aggregate, Map countMap) { + if (aggregate.isSterms()) { + processTermsAggregate(aggregate, countMap); + } else if (aggregate.isDateRange()) { + processDateRangeAggregate(aggregate, countMap); + } + } + + private void processTermsAggregate(Aggregate aggregate, Map countMap) { + aggregate.sterms().buckets().array().forEach(bucket -> + countMap.put(bucket.key().stringValue(), bucket.docCount()) + ); + } + + private void processDateRangeAggregate(Aggregate aggregate, Map countMap) { + aggregate.dateRange().buckets().array().stream() + .filter(bucket -> bucket.docCount() != 0) + .forEach(bucket -> + countMap.put( + bucket.fromAsString() + " - " + bucket.toAsString(), + bucket.docCount() + ) + ); + } } From 1300ff20ee85e71faac1dd96afc30cd3fd8e1325 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 18:32:54 +0530 Subject: [PATCH 06/11] adds swagger documentation Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../mongoes/web/controller/SearchController.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java index 0b4c0949f..af1c24ff4 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java @@ -152,12 +152,16 @@ public Mono> aggregateSearch( description = "Find restaurants within specified distance from given coordinates") @GetMapping("/search/restaurant/withInRange") public Flux searchRestaurantsWithInRange( - @Parameter(description = "Latitude coordinate") @RequestParam Double lat, - @Parameter(description = "Longitude coordinate") @RequestParam Double lon, - @Parameter(description = "Distance from coordinates") @RequestParam Double distance, - @Parameter(description = "Unit of distance (km/mi)", example = "km") - @RequestParam(defaultValue = "km", required = false) - String unit) { + @Parameter(description = "Latitude coordinate (between -90 and 90)", example = "40.7128") + @RequestParam @Min(-90) @Max(90) Double lat, + @Parameter(description = "Longitude coordinate (between -180 and 180)", example = "-74.0060") + @RequestParam @Min(-180) @Max(180) Double lon, + @Parameter(description = "Distance from coordinates (must be positive)") + @RequestParam @Positive Double distance, + @Parameter(description = "Unit of distance", example = "km", schema = @Schema(allowableValues = {"km", "mi"})) + @RequestParam(defaultValue = "km", required = false) + @Pattern(regexp = "^(km|mi)$", message = "Unit must be either 'km' or 'mi'") + String unit) { return this.searchService.searchRestaurantsWithInRange(lat, lon, distance, unit); } } From f6bdd32fb258522e9f313b129a5d466cb8d45b6d Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 13:09:49 +0000 Subject: [PATCH 07/11] add documentation --- .../web/controller/RestaurantController.java | 4 +- .../web/controller/SearchController.java | 49 +++++++++++++++---- .../web/service/AggregationProcessor.java | 30 +++++++----- 3 files changed, 59 insertions(+), 24 deletions(-) diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java index 76a39deea..1bf947d39 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java @@ -84,7 +84,9 @@ public Mono> createRestaurant( .map( restaurant -> ResponseEntity.created( - URI.create("/restaurant/" + restaurant.getName())) + URI.create( + "/api/restaurant/name/" + + restaurant.getName())) .body( new GenericMessage( "restaurant with name %s created" diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java index af1c24ff4..388c4afe4 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/SearchController.java @@ -7,6 +7,13 @@ import io.micrometer.core.annotation.Timed; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Positive; import java.util.List; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.http.ResponseEntity; @@ -149,19 +156,41 @@ public Mono> aggregateSearch( @Operation( summary = "Search restaurants within range", - description = "Find restaurants within specified distance from given coordinates") + description = "Find restaurants within specified distance from given coordinates", + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully retrieved restaurants", + content = @Content(schema = @Schema(implementation = ResultData.class))), + @ApiResponse(responseCode = "400", description = "Invalid parameters provided") + }) @GetMapping("/search/restaurant/withInRange") public Flux searchRestaurantsWithInRange( - @Parameter(description = "Latitude coordinate (between -90 and 90)", example = "40.7128") - @RequestParam @Min(-90) @Max(90) Double lat, - @Parameter(description = "Longitude coordinate (between -180 and 180)", example = "-74.0060") - @RequestParam @Min(-180) @Max(180) Double lon, + @Parameter( + description = "Latitude coordinate (between -90 and 90)", + example = "40.7128") + @RequestParam + @Min(-90) + @Max(90) + Double lat, + @Parameter( + description = "Longitude coordinate (between -180 and 180)", + example = "-74.0060") + @RequestParam + @Min(-180) + @Max(180) + Double lon, @Parameter(description = "Distance from coordinates (must be positive)") - @RequestParam @Positive Double distance, - @Parameter(description = "Unit of distance", example = "km", schema = @Schema(allowableValues = {"km", "mi"})) - @RequestParam(defaultValue = "km", required = false) - @Pattern(regexp = "^(km|mi)$", message = "Unit must be either 'km' or 'mi'") - String unit) { + @RequestParam + @Positive + Double distance, + @Parameter( + description = "Unit of distance", + example = "km", + schema = @Schema(allowableValues = {"km", "mi"})) + @RequestParam(defaultValue = "km", required = false) + @Pattern(regexp = "^(km|mi)$", message = "Unit must be either 'km' or 'mi'") + String unit) { return this.searchService.searchRestaurantsWithInRange(lat, lon, distance, unit); } } diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java index a3a021c0e..685393cb0 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java @@ -1,13 +1,15 @@ package com.example.mongoes.web.service; import co.elastic.clients.elasticsearch._types.aggregations.Aggregate; -import co.elastic.clients.elasticsearch._types.aggregations.RangeBucket; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregation; import org.springframework.stereotype.Service; +/** + * Processes Elasticsearch aggregations and transforms them into a structured map format. Supports + * 'terms' and 'dateRange' aggregation types. + */ @Service class AggregationProcessor { @@ -15,7 +17,8 @@ class AggregationProcessor { * Processes Elasticsearch aggregations and returns a structured map of results. * * @param aggregationMap Map of aggregation key to ElasticsearchAggregation - * @return Map of aggregation key to counts, where counts is a map of bucket key to document count + * @return Map of aggregation key to counts, where counts is a map of bucket key to document + * count * @throws IllegalArgumentException if aggregationMap is null */ public Map> processAggregations( @@ -43,19 +46,20 @@ private void processAggregate(Aggregate aggregate, Map countMap) { } private void processTermsAggregate(Aggregate aggregate, Map countMap) { - aggregate.sterms().buckets().array().forEach(bucket -> - countMap.put(bucket.key().stringValue(), bucket.docCount()) - ); + aggregate + .sterms() + .buckets() + .array() + .forEach(bucket -> countMap.put(bucket.key().stringValue(), bucket.docCount())); } private void processDateRangeAggregate(Aggregate aggregate, Map countMap) { aggregate.dateRange().buckets().array().stream() - .filter(bucket -> bucket.docCount() != 0) - .forEach(bucket -> - countMap.put( - bucket.fromAsString() + " - " + bucket.toAsString(), - bucket.docCount() - ) - ); + .filter(bucket -> bucket.docCount() != 0) + .forEach( + bucket -> + countMap.put( + bucket.fromAsString() + " - " + bucket.toAsString(), + bucket.docCount())); } } From 76bdecc98f7c8c39416428be8df7989a2d84bb44 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 13:14:21 +0000 Subject: [PATCH 08/11] feat : convert to nonblocking io --- .../web/service/RestaurantService.java | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java index b6f64500d..0c9d49f79 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java @@ -67,38 +67,33 @@ public Flux loadData() throws IOException { } private Flux saveAll(List restaurantStringList) { - List restaurantList = - restaurantStringList.stream() - .map(Document::parse) - .map( - document -> { - Restaurant restaurant = new Restaurant(); - restaurant.setRestaurantId( - Long.valueOf( - document.get("restaurant_id", String.class))); - restaurant.setName(document.get("name", String.class)); - restaurant.setCuisine(document.get("cuisine", String.class)); - restaurant.setBorough(document.get("borough", String.class)); - Address address = new Address(); - Document addressDoc = (Document) document.get("address"); - address.setBuilding(addressDoc.get("building", String.class)); - address.setStreet(addressDoc.get("street", String.class)); - address.setZipcode( - Integer.valueOf( - addressDoc.get("zipcode", String.class))); - List obj = addressDoc.getList("coord", Double.class); - Point geoJsonPoint = new Point(obj.getFirst(), obj.get(1)); - address.setLocation(geoJsonPoint); - restaurant.setAddress(address); - List gradesList = - getGradesList( - document.getList("grades", Document.class)); - restaurant.setGrades(gradesList); - - return restaurant; - }) - .toList(); - return restaurantRepository.saveAll(restaurantList); + return Flux.fromIterable(restaurantStringList) + .map(Document::parse) + .map(this::documentToRestaurant) + .flatMap(restaurantRepository::save); + } + + private Restaurant documentToRestaurant(Document document) { + Restaurant restaurant = new Restaurant(); + restaurant.setRestaurantId(Long.valueOf(document.get("restaurant_id", String.class))); + restaurant.setName(document.get("name", String.class)); + restaurant.setCuisine(document.get("cuisine", String.class)); + restaurant.setBorough(document.get("borough", String.class)); + + Address address = new Address(); + Document addressDoc = document.get("address", Document.class); + address.setBuilding(addressDoc.get("building", String.class)); + address.setStreet(addressDoc.get("street", String.class)); + address.setZipcode(Integer.valueOf(addressDoc.get("zipcode", String.class))); + List coord = addressDoc.getList("coord", Double.class); + Point geoJsonPoint = new Point(coord.get(0), coord.get(1)); + address.setLocation(geoJsonPoint); + restaurant.setAddress(address); + + List gradesList = getGradesList(document.getList("grades", Document.class)); + restaurant.setGrades(gradesList); + + return restaurant; } private List getGradesList(List gradeDocumentList) { From e011b6f04cccd436b286497f2c9cd973c277f7e5 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 14:27:24 +0000 Subject: [PATCH 09/11] feat : handle exception --- .vscode/launch.json | 10 ++++ .../config/GlobalExceptionHandler.java | 18 +++++++ .../web/controller/RestaurantController.java | 5 +- .../DuplicateRestaurantException.java | 8 ++++ .../web/service/RestaurantService.java | 47 ++++++++++++++----- 5 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/GlobalExceptionHandler.java create mode 100644 boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/exception/DuplicateRestaurantException.java diff --git a/.vscode/launch.json b/.vscode/launch.json index 4d8a521d8..7d7746e98 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -170,6 +170,16 @@ "projectName": "multidatasource-multitenancy", "args": "", "envFile": "${workspaceFolder}/.env" + }, + { + "type": "java", + "name": "Spring Boot-TestMongoESApplication", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.example.mongoes.TestMongoESApplication", + "projectName": "boot-mongodb-elasticsearch", + "args": "", + "envFile": "${workspaceFolder}/.env" } ] } \ No newline at end of file diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/GlobalExceptionHandler.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/GlobalExceptionHandler.java new file mode 100644 index 000000000..b70d45cab --- /dev/null +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/config/GlobalExceptionHandler.java @@ -0,0 +1,18 @@ +package com.example.mongoes.config; + +import com.example.mongoes.response.GenericMessage; +import com.example.mongoes.web.exception.DuplicateRestaurantException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(DuplicateRestaurantException.class) + public ResponseEntity handleDuplicateRestaurantException( + DuplicateRestaurantException ex) { + return ResponseEntity.status(HttpStatus.CONFLICT).body(new GenericMessage(ex.getMessage())); + } +} diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java index 1bf947d39..ca3c53192 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java @@ -85,8 +85,9 @@ public Mono> createRestaurant( restaurant -> ResponseEntity.created( URI.create( - "/api/restaurant/name/" - + restaurant.getName())) + String.format( + "/api/restaurant/name/%s", + restaurantRequest.name()))) .body( new GenericMessage( "restaurant with name %s created" diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/exception/DuplicateRestaurantException.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/exception/DuplicateRestaurantException.java new file mode 100644 index 000000000..fadc4ce82 --- /dev/null +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/exception/DuplicateRestaurantException.java @@ -0,0 +1,8 @@ +package com.example.mongoes.web.exception; + +public class DuplicateRestaurantException extends RuntimeException { + + public DuplicateRestaurantException(String message) { + super(message); + } +} diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java index 0c9d49f79..4894861c9 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java @@ -9,14 +9,14 @@ import com.example.mongoes.mongodb.repository.RestaurantRepository; import com.example.mongoes.utils.AppConstants; import com.example.mongoes.utils.DateUtility; +import com.example.mongoes.web.exception.DuplicateRestaurantException; import com.example.mongoes.web.model.RestaurantRequest; import com.mongodb.client.model.changestream.ChangeStreamDocument; import com.mongodb.client.model.changestream.OperationType; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.Instant; +import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Objects; @@ -25,7 +25,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -38,6 +39,7 @@ import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; @Service public class RestaurantService { @@ -59,11 +61,23 @@ public RestaurantService( this.reactiveMongoTemplate = reactiveMongoTemplate; } - public Flux loadData() throws IOException { - Resource input = new ClassPathResource("restaurants.json"); - Path path = input.getFile().toPath(); - var restaurantArray = Files.readAllLines(path); - return this.saveAll(restaurantArray); + public Flux loadData() { + return DataBufferUtils.read( + new ClassPathResource("restaurants.json"), + new DefaultDataBufferFactory(), + 4096) + .map( + dataBuffer -> { + byte[] bytes = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(bytes); + DataBufferUtils.release(dataBuffer); + return new String(bytes, StandardCharsets.UTF_8); + }) + .flatMap( + fileContent -> { + List restaurantArray = Arrays.asList(fileContent.split("\n")); + return this.saveAll(restaurantArray); + }); } private Flux saveAll(List restaurantStringList) { @@ -145,6 +159,7 @@ public Flux> changeStreamProcessor() { .resumeAt(getChangeStreamOption()) .listen() .delayElements(Duration.ofMillis(5)) + .publishOn(Schedulers.boundedElastic()) .doOnNext( restaurantChangeStreamEvent -> { log.info( @@ -213,9 +228,17 @@ public Mono> findAllRestaurants(int offset, int limit) { return this.restaurantESRepository.findAll(pageable); } - public Mono createRestaurant(RestaurantRequest restaurantRequest) { - - return save(restaurantRequest.toRestaurant()); + public Mono createRestaurant(RestaurantRequest restaurantRequest) { + return restaurantESRepository + .findByName(restaurantRequest.name()) + .flatMap( + existingRestaurant -> + Mono.error( + new DuplicateRestaurantException( + "Restaurant with name " + + restaurantRequest.name() + + " already exists"))) + .switchIfEmpty(restaurantRepository.save(restaurantRequest.toRestaurant())); } public Mono deleteAll() { From 6337a5849d414a41c0831f2668cd5d0236cff492 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 14:30:51 +0000 Subject: [PATCH 10/11] fix : issue with loading all data --- .../mongoes/web/service/RestaurantService.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java index 4894861c9..76a910c8f 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/RestaurantService.java @@ -62,10 +62,11 @@ public RestaurantService( } public Flux loadData() { - return DataBufferUtils.read( - new ClassPathResource("restaurants.json"), - new DefaultDataBufferFactory(), - 4096) + return DataBufferUtils.join( + DataBufferUtils.read( + new ClassPathResource("restaurants.json"), + new DefaultDataBufferFactory(), + 4096)) .map( dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; @@ -73,7 +74,7 @@ public Flux loadData() { DataBufferUtils.release(dataBuffer); return new String(bytes, StandardCharsets.UTF_8); }) - .flatMap( + .flatMapMany( fileContent -> { List restaurantArray = Arrays.asList(fileContent.split("\n")); return this.saveAll(restaurantArray); From b99c3222c7b5f6361205a6197537a3497743182a Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Dec 2024 14:47:45 +0000 Subject: [PATCH 11/11] implement code review comments --- .../mongoes/web/controller/RestaurantController.java | 6 +++++- .../mongoes/web/service/AggregationProcessor.java | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java index ca3c53192..caede0a4c 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/controller/RestaurantController.java @@ -9,6 +9,8 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.Size; import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -87,7 +89,9 @@ public Mono> createRestaurant( URI.create( String.format( "/api/restaurant/name/%s", - restaurantRequest.name()))) + URLEncoder.encode( + restaurantRequest.name(), + StandardCharsets.UTF_8)))) .body( new GenericMessage( "restaurant with name %s created" diff --git a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java index 685393cb0..9f4c341c1 100644 --- a/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java +++ b/boot-mongodb-elasticsearch/src/main/java/com/example/mongoes/web/service/AggregationProcessor.java @@ -3,16 +3,23 @@ import co.elastic.clients.elasticsearch._types.aggregations.Aggregate; import java.util.HashMap; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregation; import org.springframework.stereotype.Service; /** * Processes Elasticsearch aggregations and transforms them into a structured map format. Supports * 'terms' and 'dateRange' aggregation types. + * + *

Example output format: { "termAggregation": {"term1": 10, "term2": 20}, + * "dateRangeAggregation": {"2023-01-01 - 2023-12-31": 100} } */ @Service class AggregationProcessor { + private static final Logger log = LoggerFactory.getLogger(AggregationProcessor.class); + /** * Processes Elasticsearch aggregations and returns a structured map of results. * @@ -42,6 +49,10 @@ private void processAggregate(Aggregate aggregate, Map countMap) { processTermsAggregate(aggregate, countMap); } else if (aggregate.isDateRange()) { processDateRangeAggregate(aggregate, countMap); + } else { + log.debug( + "Unsupported aggregation type encountered: {}", + aggregate.getClass().getSimpleName()); } }