This is the code I came across in our repository. This performs search in-memory and is invoked on service layer code.
In our front end operators like EQUAL_TO
and so on are passed with field name and field values are passed as list of parameters in the query.
This is autowired and passed in the service calls like so
return paginate(SearchEngine.search(pgRatingResponses, operator, fieldName, fieldValues), pageable);
public class SearchEngine {
/**
* Valid pattern.
*/
private static final String REGEX = "^[A-Z_a-z0-9 ]*$";
/**
* Searches of records.
*
* @param records
* @param operator
* @param fieldName
* @param fieldValues
* @param <T>
* @return
*/
public <T> List<T> search(final List<T> records, final SearchOperator operator, final String fieldName,
final List<String> fieldValues) {
List<T> results = new ArrayList<>();
if (Objects.nonNull(records) && Objects.nonNull(operator) && Objects.nonNull(fieldName)
&& StringUtils.hasLength(fieldName) && Objects.nonNull(fieldValues) && !fieldValues.isEmpty()
&& !records.isEmpty()) {
switch (operator) {
case EQUAL_TO:
results = records.stream()
.filter(record -> fieldValues.stream().filter(Objects::nonNull).map(String::trim)
.filter(StringUtils::hasLength).distinct()
.filter(fieldValue -> Pattern.matches(REGEX, fieldValue))
.anyMatch(fieldValue -> isEqual(fieldName.trim(), fieldValue, record)))
.collect(Collectors.toList());
break;
case NOT_EQUAL_TO:
results = records.stream()
.filter(record -> fieldValues.stream().filter(Objects::nonNull).map(String::trim)
.filter(StringUtils::hasLength).distinct()
.filter(fieldValue -> Pattern.matches(REGEX, fieldValue))
.allMatch(fieldValue -> !isEqual(fieldName.trim(), fieldValue, record)))
.collect(Collectors.toList());
break;
case CONTAINS:
results = records.stream()
.filter(record -> fieldValues.stream().filter(Objects::nonNull).map(String::trim)
.filter(StringUtils::hasLength).distinct()
.filter(fieldValue -> Pattern.matches(REGEX, fieldValue))
.anyMatch(fieldValue -> contains(fieldName.trim(), fieldValue, record)))
.collect(Collectors.toList());
break;
case DOES_NOT_CONTAIN:
results = records.stream()
.filter(record -> fieldValues.stream().filter(Objects::nonNull).map(String::trim)
.filter(StringUtils::hasLength).distinct()
.filter(fieldValue -> Pattern.matches(REGEX, fieldValue))
.allMatch(fieldValue -> !contains(fieldName.trim(), fieldValue, record)))
.collect(Collectors.toList());
break;
default:
throw new IllegalArgumentException(ErrorConstants.PARAMETER_VALUE_MISMATCHING);
}
} else if (Objects.nonNull(records)) {
results = records;
}
return results;
}
/**
* Checks for equality.
*
* @param <T>
* @param fieldName
* @param fieldValue
* @param record
* @return
*/
private <T> boolean isEqual(final String fieldName, final String fieldValue, final T record) {
try {
for (Field field : record.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (field.getName().equals(fieldName) && Objects.nonNull(field.get(record))) {
if (field.getType().equals(String.class)) {
return field.get(record).toString().trim().equalsIgnoreCase(fieldValue);
} else {
return field.get(record).equals(fieldValue);
}
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
log.error(ErrorConstants.PARAMETER_VALUE_MISMATCHING + e.getLocalizedMessage());
}
return false;
}
/**
* Checks for pattern matchings.
*
* @param <T>
* @param fieldName
* @param fieldValue
* @param record
* @return
*/
private <T> boolean contains(final String fieldName, final String fieldValue, final T record) {
try {
for (Field field : record.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (field.getName().equals(fieldName) && Objects.nonNull(field.get(record))) {
return Pattern.matches(".*" + fieldValue.toLowerCase() + ".*",
field.get(record).toString().trim().toLowerCase());
}
}
} catch (IllegalArgumentException | IllegalAccessException e) {
log.error(ErrorConstants.PARAMETER_VALUE_MISMATCHING + e.getLocalizedMessage());
}
return false;
}
}