Consider an "Activity"entity with below String fields, to include in our search:
1. Controller class
@PostMapping("/search")
public ResponseEntity<List<ActivityDTO>> searchAvailableActivities(@Valid @RequestBody SearchDTO searchDTO) {
final List<ActivityDTO> workplaceDTOList = searchService.searchAvailableActivities(searchDTO);
return new ResponseEntity<>(workplaceDTOList, HttpStatus.OK);
}
@PostMapping("/search")
public ResponseEntity<List<ActivityDTO>> searchAvailableActivities(@Valid @RequestBody SearchDTO searchDTO) {
final List<ActivityDTO> workplaceDTOList = searchService.searchAvailableActivities(searchDTO);
return new ResponseEntity<>(workplaceDTOList, HttpStatus.OK);
}
2. Service class
public Page<ActivityDTO> searchAvailableActivities(SearchDTO searchDTO) {
List<ActivityDTO> activityList = this.searchAvailableActivitiesMethod(searchDTO);
return activityList;
}
// Returns activities entities converted to DTOs
public List<ActivityDTO> searchAvailableActivitiesMethod(SearchDTO searchDTO) {
List<Activity> activityList = searchAvailableActivitiesLogic(searchDTO);
return activityList.stream().map(activityMapper::toDto).collect(Collectors.toList());
}
// Finds activities from search query's strings
public List<Activity> searchAvailableActivitiesLogic(SearchDTO searchDTO) {
Set<Activity> activityList = searchActivities(getSearchTerms(searchDTO.getSearchQuery()));
return new ArrayList<>(activityList);
}
// Retrieves search words separated by comma
public List<String> getSearchTerms(String searchQuery) {
if (searchQuery == null || searchQuery.isEmpty()) {
return new ArrayList<>();
}
return Arrays.stream(searchQuery.split(",")).map(String::trim).collect(Collectors.toList());
}
// Gathers found activities in Set, to avoid duplicates
public Set<Activity> searchActivities(List<String> searchTerms) {
if (searchTerms == null || searchTerms.isEmpty() || searchTerms.stream().allMatch(String::isEmpty)) {
return new HashSet<>();
}
return new HashSet<>(searchContainsTerms(searchTerms));
}
// Executes searching per query's term, using JpaSpecifications
public List<Activity> searchContainsTerms(List<String> searchTerms) {
if (searchTerms == null || searchTerms.isEmpty() || searchTerms.stream().allMatch(String::isEmpty)) {
return new ArrayList<>();
}
List<Activity> result = new ArrayList<Activity>();
for (String term : searchTerms) {
Iterable<Activity> activities = this.activityRepository
.findAll(CustomJpaSpecifications.hasTitleWithContainingTerm(term)
.or(CustomJpaSpecifications.hasSubActivityWithContainingTerm(term))
);
for (Activity act : activities) {
result.add(act);
}
}
return result;
}
3. Repository class
public class CustomJpaSpecifications {
public static Specification<Activity> hasSubActivityWithContainingTerm(String term) {
return (act, cq, cb) -> cb.like(act.get("activitySubType"), "%" + term + "%");
}
public static Specification<Activity> hasTitleWithContainingTerm(String term) {
return (act, cq, cb) -> cb.like(act.get("title"), "%" + term + "%");
}
}