Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(FSADT1-1528): updating complex search from predictive #1219

Merged
merged 8 commits into from
Oct 8, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Up @@ -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
Expand All @@ -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
Expand Up @@ -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;
Expand Down Expand Up @@ -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()));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -281,6 +282,8 @@ void shouldSearchClientName(
@DisplayName("Search using the predictive search")
void shouldSearchPredicatively(
String searchValue,
Integer page,
Integer size,
String expectedClientNumber,
String expectedClientName
) {
Expand All @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -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)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)
Expand Down
Loading