Skip to content

Commit

Permalink
feat(FSADT1-1528): updating complex search from predictive (#1219)
Browse files Browse the repository at this point in the history
* feat(FSADT1-1528): added external pagination info for predictive

* feat(FSADT1-1528): updated predictive endpoint to complex

- Updated endpoint to be just search
- Updated the controller to be able to search for latest
- Updated query to include pagination
- Added default page values for predictive
- Changed tests

* fix(FSADT1-1528): fixing search

* test: fixing tests

* Made code reviews

---------

Co-authored-by: Maria Martinez <[email protected]>
paulushcgcj and mamartinezmejia authored Oct 8, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent c80723a commit 550c941
Showing 5 changed files with 142 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -137,14 +139,19 @@ public Flux<ForestClientDto> findByClientName(
return service.findByClientName(clientName);
}

@GetMapping("/predictive")
public Flux<PredictiveSearchResultDto> findByPredictiveSearch(
@RequestParam String value
){
log.info("Receiving request to search by predictive search {}", value);
return service.predictiveSearch(value);
}


@GetMapping
public Flux<PredictiveSearchResultDto> findByComplexSearch(
@RequestParam(required = false) String value,
@RequestParam(required = false, defaultValue = "0") Integer page,
@RequestParam(required = false, defaultValue = "5") Integer size) {
if (StringUtils.isNotBlank(value)) {
log.info("Receiving request to do a complex search by {}", value);
return service.complexSearch(value, PageRequest.of(page, size));
} else {
log.info("Receiving request to search the latest entries");
return service.latestEntries(PageRequest.of(page, size));
}

}

}
Original file line number Diff line number Diff line change
@@ -72,15 +72,15 @@ Flux<ForestClientEntity> findClientByIncorporationOrName(
cl.city as city,
ctc.description as client_type,
csc.description as client_status,
(
CASE WHEN c.client_number = :value THEN 112 ELSE 0 END +
CASE WHEN c.CLIENT_ACRONYM = :value THEN 111 ELSE 0 END +
(UTL_MATCH.JARO_WINKLER_SIMILARITY(c.client_name, :value)+10) +
(UTL_MATCH.JARO_WINKLER_SIMILARITY(c.legal_first_name, :value)+9) +
(UTL_MATCH.JARO_WINKLER_SIMILARITY(dba.doing_business_as_name, :value)+7) +
CASE WHEN c.client_identification = :value THEN 106 ELSE 0 END +
UTL_MATCH.JARO_WINKLER_SIMILARITY(c.legal_middle_name, :value)
) AS score
(
CASE WHEN c.client_number = :value THEN 112 ELSE 0 END +
CASE WHEN c.CLIENT_ACRONYM = :value THEN 111 ELSE 0 END +
(UTL_MATCH.JARO_WINKLER_SIMILARITY(c.client_name, :value)+10) +
(UTL_MATCH.JARO_WINKLER_SIMILARITY(c.legal_first_name, :value)+9) +
(UTL_MATCH.JARO_WINKLER_SIMILARITY(dba.doing_business_as_name, :value)+7) +
CASE WHEN c.client_identification = :value THEN 106 ELSE 0 END +
UTL_MATCH.JARO_WINKLER_SIMILARITY(c.legal_middle_name, :value)
) AS score
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
@@ -97,10 +97,35 @@ Flux<ForestClientEntity> findClientByIncorporationOrName(
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'
ORDER BY score DESC
FETCH FIRST 5 ROWS ONLY""")
Flux<PredictiveSearchResultDto> findByPredictiveSearch(String value);
OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY""")
Flux<PredictiveSearchResultDto> findByPredictiveSearch(String value, int limit, long offset);

@Query("""
SELECT
c.client_number,
c.CLIENT_ACRONYM as client_acronym,
c.client_name,
c.legal_first_name as client_first_name,
dba.doing_business_as_name as doing_business_as,
c.client_identification,
c.legal_middle_name as client_middle_name,
cl.city as city,
ctc.description as client_type,
csc.description as status_code,
100 as score
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'
ORDER BY c.ADD_TIMESTAMP DESC
OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY""")
Flux<PredictiveSearchResultDto> findByEmptyFullSearch(int limit, long offset);

}
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Criteria;
@@ -532,18 +533,22 @@ public Flux<ForestClientDto> findByClientName(String clientName) {
);
}

public Flux<PredictiveSearchResultDto> predictiveSearch(String value){
log.info("Predictive search for value {}", value);
public Flux<PredictiveSearchResultDto> 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())
.doOnNext(dto -> log.info("Found predictive search for value {} as {} {} with score {}",
value,
dto.clientNumber(), dto.name(), dto.score()
)
);
.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()));
}

public Flux<PredictiveSearchResultDto> 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()));
}

/**
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@@ -281,6 +282,8 @@ void shouldSearchClientName(
@DisplayName("Search using the predictive search")
void shouldSearchPredicatively(
String searchValue,
Integer page,
Integer size,
String expectedClientNumber,
String expectedClientName
) {
@@ -290,8 +293,10 @@ void shouldSearchPredicatively(
.get()
.uri(uriBuilder ->
uriBuilder
.path("/api/search/predictive")
.queryParam("value", Optional.ofNullable(searchValue))
.path("/api/search")
.queryParamIfPresent("value", Optional.ofNullable(searchValue))
.queryParamIfPresent("page", Optional.ofNullable(page))
.queryParamIfPresent("size", Optional.ofNullable(size))
.build(new HashMap<>())
)
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
@@ -306,14 +311,41 @@ void shouldSearchPredicatively(
.jsonPath("$[0].clientName").isNotEmpty()
.jsonPath("$[0].name").isEqualTo(expectedClientName)
.consumeWith(System.out::println);
}else{
} else {
response.expectStatus().isOk()
.expectBody()
.expectBody()
.consumeWith(System.out::println).json("[]");
}

}

@Test
@DisplayName("Search using the predictive search")
void shouldSearchEmpty() {

ResponseSpec response =
client
.get()
.uri(uriBuilder ->
uriBuilder
.path("/api/search")
.queryParam("page", 0)
.queryParam("size", 10)
.build(new HashMap<>())
)
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.exchange();

response
.expectStatus().isOk()
.expectBody()
.jsonPath("$[0].clientNumber").isNotEmpty()
.jsonPath("$[0].clientName").isNotEmpty()
.jsonPath("$.length()").isEqualTo(10)
.consumeWith(System.out::println);

}

private static Stream<Arguments> byEmail() {
return
Stream.concat(
@@ -497,39 +529,48 @@ private static Stream<Arguments> clientName() {

private static Stream<Arguments> doingBusinessAs() {
return
Stream
.of(
Arguments.of(null,null, null, MissingRequiredParameterException.class),
Arguments.of(null,false, null, MissingRequiredParameterException.class),
Arguments.of(null,true, null, MissingRequiredParameterException.class),
Arguments.of(StringUtils.EMPTY, null, null, MissingRequiredParameterException.class),
Arguments.of(StringUtils.EMPTY, false, null, MissingRequiredParameterException.class),
Arguments.of(StringUtils.EMPTY, true, null, MissingRequiredParameterException.class),
Arguments.of(" ", null, null, MissingRequiredParameterException.class),
Arguments.of(" ", false, null, MissingRequiredParameterException.class),
Arguments.of(" ", true, null, MissingRequiredParameterException.class),

Arguments.of("BORIS AND BORIS INC.", null, "00000003", null),
Arguments.of("BORIS AND BORIS INC.", false, "00000003", null),
Arguments.of("BORIS AND BORIS", true, "00000003", null),

Arguments.of("ELARICHO", null, "00000005", null),
Arguments.of("ELARICHO", false, "00000005", null),
Arguments.of("ELARICHO", true, "00000005", null),

Arguments.of("ELARICO", true, "00000005", null),
Arguments.of("ELACHO", true, StringUtils.EMPTY, null),
Arguments.of("ELARICO", false, StringUtils.EMPTY, null)
);
Stream
.of(
Arguments.of(null, null, null, MissingRequiredParameterException.class),
Arguments.of(null, false, null, MissingRequiredParameterException.class),
Arguments.of(null, true, null, MissingRequiredParameterException.class),
Arguments.of(StringUtils.EMPTY, null, null,
MissingRequiredParameterException.class),
Arguments.of(StringUtils.EMPTY, false, null,
MissingRequiredParameterException.class),
Arguments.of(StringUtils.EMPTY, true, null,
MissingRequiredParameterException.class),
Arguments.of(" ", null, null, MissingRequiredParameterException.class),
Arguments.of(" ", false, null, MissingRequiredParameterException.class),
Arguments.of(" ", true, null, MissingRequiredParameterException.class),

Arguments.of("BORIS AND BORIS INC.", null, "00000003", null),
Arguments.of("BORIS AND BORIS INC.", false, "00000003", null),
Arguments.of("BORIS AND BORIS", true, "00000003", null),

Arguments.of("ELARICHO", null, "00000005", null),
Arguments.of("ELARICHO", false, "00000005", null),
Arguments.of("ELARICHO", true, "00000005", null),

Arguments.of("ELARICO", true, "00000005", null),
Arguments.of("ELACHO", true, StringUtils.EMPTY, null),
Arguments.of("ELARICO", false, StringUtils.EMPTY, null)
);
}

private static Stream<Arguments> byPredictive() {
return Stream
.of(
Arguments.of("pollich", "00000114", "POLLICH-ABERNATHY"),
Arguments.of("kilback", "00000123", "REICHERT, KILBACK AND EMARD"),
Arguments.of("darbie", "00000145", "DARBIE BLIND (MINYX)"),
Arguments.of("pietro", StringUtils.EMPTY, StringUtils.EMPTY)
Arguments.of("pollich", null, null, "00000114", "POLLICH-ABERNATHY"),
Arguments.of("pollich", 0, 2, "00000114", "POLLICH-ABERNATHY"),
Arguments.of("pollich", 4, 10, StringUtils.EMPTY, StringUtils.EMPTY),
Arguments.of("kilback", null, null, "00000123", "REICHERT, KILBACK AND EMARD"),
Arguments.of("kilback", 0, 1, "00000123", "REICHERT, KILBACK AND EMARD"),
Arguments.of("darbie", null, null, "00000145", "DARBIE BLIND (MINYX)"),
Arguments.of("darbie", 0, 4, "00000145", "DARBIE BLIND (MINYX)"),
Arguments.of("pietro", null, null, StringUtils.EMPTY, StringUtils.EMPTY),
Arguments.of("pietro", 0, 5, StringUtils.EMPTY, StringUtils.EMPTY),
Arguments.of("pietro", 4, 10, StringUtils.EMPTY, StringUtils.EMPTY)
);
}

Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import reactor.test.StepVerifier;
import reactor.test.StepVerifier.FirstStep;

@@ -101,15 +102,15 @@ void shouldFindByContact(
@DisplayName("should do predictive search")
@ParameterizedTest
@MethodSource("byPredictive")
void shouldSearchWithPredictiveSearch(
void shouldSearchWithComplexSearch(
String searchValue,
String expectedClientNumber,
String expectedClientName
) {

FirstStep<PredictiveSearchResultDto> test =
service
.predictiveSearch(searchValue)
.complexSearch(searchValue, PageRequest.of(0, 5))
.as(StepVerifier::create);

if(StringUtils.isNotBlank(expectedClientNumber)) {
@@ -322,7 +323,7 @@ private static Stream<Arguments> emptyCases() {
private static Stream<Arguments> byPredictive() {
return Stream
.of(
Arguments.of("indian canada", "00000006", "INDIAN CANADA"),
Arguments.of("pollich", "00000114", "POLLICH-ABERNATHY"),
Arguments.of("kilback", "00000123", "REICHERT, KILBACK AND EMARD"),
Arguments.of("darbie", "00000145", "DARBIE BLIND (MINYX)"),
Arguments.of("pietro", StringUtils.EMPTY, StringUtils.EMPTY)

0 comments on commit 550c941

Please sign in to comment.