From 6397fdd675d228adbf55e89f301c342a98397fa1 Mon Sep 17 00:00:00 2001 From: Paulo Gomes da Cruz Junior Date: Wed, 9 Oct 2024 09:07:26 -0700 Subject: [PATCH 1/2] fix(FSADT1-1528): added missing count --- .../controller/ClientSearchController.java | 20 +++++++++-- .../repository/ForestClientRepository.java | 36 +++++++++++++++++++ .../gov/app/service/ClientSearchService.java | 36 +++++++++++++------ ...ClientSearchControllerIntegrationTest.java | 4 +++ .../ClientSearchServiceIntegrationTest.java | 9 +++-- 5 files changed, 89 insertions(+), 16 deletions(-) diff --git a/legacy/src/main/java/ca/bc/gov/app/controller/ClientSearchController.java b/legacy/src/main/java/ca/bc/gov/app/controller/ClientSearchController.java index 296b225e93..6c3c74277b 100644 --- a/legacy/src/main/java/ca/bc/gov/app/controller/ClientSearchController.java +++ b/legacy/src/main/java/ca/bc/gov/app/controller/ClientSearchController.java @@ -7,12 +7,15 @@ import ca.bc.gov.app.service.ClientSearchService; import io.micrometer.observation.annotation.Observed; import java.time.LocalDate; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.data.domain.PageRequest; import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -143,13 +146,24 @@ public Flux findByClientName( public Flux findByComplexSearch( @RequestParam(required = false) String value, @RequestParam(required = false, defaultValue = "0") Integer page, - @RequestParam(required = false, defaultValue = "5") Integer size) { + @RequestParam(required = false, defaultValue = "5") Integer size, + ServerHttpResponse serverResponse) { if (StringUtils.isNotBlank(value)) { log.info("Receiving request to do a complex search by {}", value); - return service.complexSearch(value, PageRequest.of(page, size)); + return service + .complexSearch(value, PageRequest.of(page, size)) + .doOnNext(pair -> serverResponse.getHeaders() + .putIfAbsent("X-Total", List.of(pair.getValue().toString())) + ) + .map(Pair::getKey); } else { log.info("Receiving request to search the latest entries"); - return service.latestEntries(PageRequest.of(page, size)); + return service + .latestEntries(PageRequest.of(page, size)) + .doOnNext(pair -> serverResponse.getHeaders() + .putIfAbsent("X-Total", List.of(pair.getValue().toString())) + ) + .map(Pair::getKey); } } diff --git a/legacy/src/main/java/ca/bc/gov/app/repository/ForestClientRepository.java b/legacy/src/main/java/ca/bc/gov/app/repository/ForestClientRepository.java index ac6c68706d..3240687965 100644 --- a/legacy/src/main/java/ca/bc/gov/app/repository/ForestClientRepository.java +++ b/legacy/src/main/java/ca/bc/gov/app/repository/ForestClientRepository.java @@ -104,6 +104,30 @@ Flux findClientByIncorporationOrName( OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY""") Flux findByPredictiveSearch(String value, int limit, long offset); + @Query(""" + SELECT + count(c.client_number) + FROM the.forest_client c + LEFT JOIN the.CLIENT_DOING_BUSINESS_AS dba ON c.client_number = dba.client_number + LEFT JOIN the.CLIENT_TYPE_CODE ctc ON c.client_type_code = ctc.client_type_code + LEFT JOIN the.CLIENT_LOCATION cl ON c.client_number = cl.client_number + LEFT JOIN the.CLIENT_STATUS_CODE csc ON c.client_status_code = csc.client_status_code + WHERE + ( + c.client_number = :value + OR c.CLIENT_ACRONYM = :value + OR UTL_MATCH.JARO_WINKLER_SIMILARITY(c.client_name,:value) >= 90 + OR c.client_name LIKE '%' || :value || '%' + OR UTL_MATCH.JARO_WINKLER_SIMILARITY(c.legal_first_name,:value) >= 90 + OR UTL_MATCH.JARO_WINKLER_SIMILARITY(dba.doing_business_as_name,:value) >= 90 + OR dba.doing_business_as_name LIKE '%' || :value || '%' + OR c.client_identification = :value + OR UTL_MATCH.JARO_WINKLER_SIMILARITY(c.legal_middle_name,:value) >= 90 + OR c.legal_middle_name LIKE '%' || :value || '%' + ) AND + cl.CLIENT_LOCN_CODE = '00'""") + Mono countByPredictiveSearch(String value); + @Query(""" SELECT c.client_number, @@ -128,4 +152,16 @@ Flux findClientByIncorporationOrName( OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY""") Flux findByEmptyFullSearch(int limit, long offset); + @Query(""" + SELECT + count(c.client_number) + FROM the.forest_client c + LEFT JOIN the.CLIENT_DOING_BUSINESS_AS dba ON c.client_number = dba.client_number + LEFT JOIN the.CLIENT_TYPE_CODE ctc ON c.client_type_code = ctc.client_type_code + LEFT JOIN the.CLIENT_LOCATION cl ON c.client_number = cl.client_number + LEFT JOIN the.CLIENT_STATUS_CODE csc ON c.client_status_code = csc.client_status_code + WHERE + cl.CLIENT_LOCN_CODE = '00'""") + Mono countByEmptyFullSearch(); + } \ No newline at end of file diff --git a/legacy/src/main/java/ca/bc/gov/app/service/ClientSearchService.java b/legacy/src/main/java/ca/bc/gov/app/service/ClientSearchService.java index 34b5f0eb9c..f8174351a8 100644 --- a/legacy/src/main/java/ca/bc/gov/app/service/ClientSearchService.java +++ b/legacy/src/main/java/ca/bc/gov/app/service/ClientSearchService.java @@ -26,6 +26,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -533,22 +534,37 @@ public Flux findByClientName(String clientName) { ); } - public Flux complexSearch(String value, Pageable page) { + public Flux> complexSearch(String value, Pageable page) { // This condition is for predictive search, and we will stop here if no query param is provided if (StringUtils.isBlank(value)) { return Flux.error(new MissingRequiredParameterException("value")); } - return forestClientRepository - .findByPredictiveSearch(value.toUpperCase(), page.getPageSize(), page.getOffset()) - .doOnNext(dto -> log.info("Found complex search for value {} as {} {} with score {}", - value, dto.clientNumber(), dto.name(), dto.score())); + return + forestClientRepository + .countByPredictiveSearch(value.toUpperCase()) + .flatMapMany(count -> + forestClientRepository + .findByPredictiveSearch(value.toUpperCase(), page.getPageSize(), + page.getOffset()) + .doOnNext( + dto -> log.info("Found complex search for value {} as {} {} with score {}", + value, dto.clientNumber(), dto.name(), dto.score()) + ) + .map(dto -> Pair.of(dto, count)) + ); } - public Flux latestEntries(Pageable page) { - return forestClientRepository - .findByEmptyFullSearch(page.getPageSize(), page.getOffset()) - .doOnNext(dto -> log.info("Found complex empty search as {} {} with score {}", - dto.clientNumber(), dto.name(), dto.score())); + public Flux> latestEntries(Pageable page) { + return + forestClientRepository + .countByEmptyFullSearch() + .flatMapMany(count -> + forestClientRepository + .findByEmptyFullSearch(page.getPageSize(), page.getOffset()) + .doOnNext(dto -> log.info("Found complex empty search as {} {} with score {}", + dto.clientNumber(), dto.name(), dto.score())) + .map(dto -> Pair.of(dto, count)) + ); } /** diff --git a/legacy/src/test/java/ca/bc/gov/app/controller/ClientSearchControllerIntegrationTest.java b/legacy/src/test/java/ca/bc/gov/app/controller/ClientSearchControllerIntegrationTest.java index 279ec17a2d..ca46912ff6 100644 --- a/legacy/src/test/java/ca/bc/gov/app/controller/ClientSearchControllerIntegrationTest.java +++ b/legacy/src/test/java/ca/bc/gov/app/controller/ClientSearchControllerIntegrationTest.java @@ -305,6 +305,8 @@ void shouldSearchPredicatively( if (StringUtils.isNotBlank(expectedClientNumber)) { response .expectStatus().isOk() + .expectHeader() + .exists("X-Total") .expectBody() .jsonPath("$[0].clientNumber").isNotEmpty() .jsonPath("$[0].clientNumber").isEqualTo(expectedClientNumber) @@ -338,6 +340,8 @@ void shouldSearchEmpty() { response .expectStatus().isOk() + .expectHeader() + .exists("X-Total") .expectBody() .jsonPath("$[0].clientNumber").isNotEmpty() .jsonPath("$[0].clientName").isNotEmpty() diff --git a/legacy/src/test/java/ca/bc/gov/app/service/ClientSearchServiceIntegrationTest.java b/legacy/src/test/java/ca/bc/gov/app/service/ClientSearchServiceIntegrationTest.java index 90e9a9ca38..66a0ceff40 100644 --- a/legacy/src/test/java/ca/bc/gov/app/service/ClientSearchServiceIntegrationTest.java +++ b/legacy/src/test/java/ca/bc/gov/app/service/ClientSearchServiceIntegrationTest.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -108,7 +109,7 @@ void shouldSearchWithComplexSearch( String expectedClientName ) { - FirstStep test = + FirstStep> test = service .complexSearch(searchValue, PageRequest.of(0, 5)) .as(StepVerifier::create); @@ -117,8 +118,10 @@ void shouldSearchWithComplexSearch( test .assertNext(dto -> { assertNotNull(dto); - assertEquals(expectedClientNumber, dto.clientNumber()); - assertEquals(expectedClientName, dto.name()); + assertEquals(expectedClientNumber, dto.getKey().clientNumber()); + assertEquals(expectedClientName, dto.getKey().name()); + assertNotNull(dto.getValue()); + assertEquals(1, dto.getValue()); }); } test.verifyComplete(); From 108a0aa5a92c87d95c3696c1d0adc3184141cde9 Mon Sep 17 00:00:00 2001 From: Maria Martinez Date: Wed, 9 Oct 2024 09:29:42 -0700 Subject: [PATCH 2/2] Renamed variable to x-total-count to keep consistency with other parts of the project --- .../java/ca/bc/gov/app/controller/ClientSearchController.java | 4 ++-- .../app/controller/ClientSearchControllerIntegrationTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/legacy/src/main/java/ca/bc/gov/app/controller/ClientSearchController.java b/legacy/src/main/java/ca/bc/gov/app/controller/ClientSearchController.java index 6c3c74277b..278e7c1608 100644 --- a/legacy/src/main/java/ca/bc/gov/app/controller/ClientSearchController.java +++ b/legacy/src/main/java/ca/bc/gov/app/controller/ClientSearchController.java @@ -153,7 +153,7 @@ public Flux findByComplexSearch( return service .complexSearch(value, PageRequest.of(page, size)) .doOnNext(pair -> serverResponse.getHeaders() - .putIfAbsent("X-Total", List.of(pair.getValue().toString())) + .putIfAbsent("X-Total-Count", List.of(pair.getValue().toString())) ) .map(Pair::getKey); } else { @@ -161,7 +161,7 @@ public Flux findByComplexSearch( return service .latestEntries(PageRequest.of(page, size)) .doOnNext(pair -> serverResponse.getHeaders() - .putIfAbsent("X-Total", List.of(pair.getValue().toString())) + .putIfAbsent("X-Total-Count", List.of(pair.getValue().toString())) ) .map(Pair::getKey); } diff --git a/legacy/src/test/java/ca/bc/gov/app/controller/ClientSearchControllerIntegrationTest.java b/legacy/src/test/java/ca/bc/gov/app/controller/ClientSearchControllerIntegrationTest.java index ca46912ff6..9a27dfc0aa 100644 --- a/legacy/src/test/java/ca/bc/gov/app/controller/ClientSearchControllerIntegrationTest.java +++ b/legacy/src/test/java/ca/bc/gov/app/controller/ClientSearchControllerIntegrationTest.java @@ -306,7 +306,7 @@ void shouldSearchPredicatively( response .expectStatus().isOk() .expectHeader() - .exists("X-Total") + .exists("X-Total-Count") .expectBody() .jsonPath("$[0].clientNumber").isNotEmpty() .jsonPath("$[0].clientNumber").isEqualTo(expectedClientNumber) @@ -341,7 +341,7 @@ void shouldSearchEmpty() { response .expectStatus().isOk() .expectHeader() - .exists("X-Total") + .exists("X-Total-Count") .expectBody() .jsonPath("$[0].clientNumber").isNotEmpty() .jsonPath("$[0].clientName").isNotEmpty()