diff --git a/pom.xml b/pom.xml index ee4c80991558c630fdfeee1bda726c032b140ffd..b4408c80712537844d77affdf892ad7bef7c2ad8 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,16 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> + <dependency> + <groupId>org.elasticsearch</groupId> + <artifactId>elasticsearch</artifactId> + <version>7.17.12</version> + </dependency> + <dependency> + <groupId>org.elasticsearch.client</groupId> + <artifactId>elasticsearch-rest-high-level-client</artifactId> + <version>7.17.12</version> <!-- Use the appropriate version --> + </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> diff --git a/src/main/java/org/upsmf/grievance/controller/SearchTicketController.java b/src/main/java/org/upsmf/grievance/controller/SearchTicketController.java index f57b910ab796a93a35193f451e9266610f0a8db2..9a041960d68a6a248dea8c786b7d2f9eddff582b 100644 --- a/src/main/java/org/upsmf/grievance/controller/SearchTicketController.java +++ b/src/main/java/org/upsmf/grievance/controller/SearchTicketController.java @@ -8,12 +8,11 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.upsmf.grievance.dto.SearchRequest; -import org.upsmf.grievance.model.es.Ticket; import org.upsmf.grievance.model.reponse.Response; import org.upsmf.grievance.model.reponse.TicketResponse; import org.upsmf.grievance.service.SearchService; -import java.util.Optional; +import java.util.Map; @Controller @RequestMapping("/search") @@ -28,5 +27,11 @@ public class SearchTicketController { Response response = new Response(HttpStatus.OK.value(), responseTicket); return new ResponseEntity<>(response, HttpStatus.OK); } + @PostMapping("/searchTickets") + public ResponseEntity<Response> searchTickets(@RequestBody SearchRequest searchRequest){ + Map<String, Object> responseTicket = searchService.searchTickets(searchRequest); + Response response = new Response(HttpStatus.OK.value(), responseTicket); + return new ResponseEntity<>(response, HttpStatus.OK); + } } \ No newline at end of file diff --git a/src/main/java/org/upsmf/grievance/dto/SearchRequest.java b/src/main/java/org/upsmf/grievance/dto/SearchRequest.java index 333a4c29abfb25be76d7b1e5eb6e6465dad41960..5a889cad4de77b0c5eee8cf1bd2afe25f56a7f79 100644 --- a/src/main/java/org/upsmf/grievance/dto/SearchRequest.java +++ b/src/main/java/org/upsmf/grievance/dto/SearchRequest.java @@ -26,6 +26,8 @@ public class SearchRequest { private Boolean isJunk; + private Boolean isEscalated; + private TicketPriority priority; private int page; diff --git a/src/main/java/org/upsmf/grievance/service/SearchService.java b/src/main/java/org/upsmf/grievance/service/SearchService.java index 1ff264eeedc78f4f6053ab340890782ef0241200..a5743e647d4f1735220435deafae22251e17c6af 100644 --- a/src/main/java/org/upsmf/grievance/service/SearchService.java +++ b/src/main/java/org/upsmf/grievance/service/SearchService.java @@ -1,11 +1,11 @@ package org.upsmf.grievance.service; -import org.upsmf.grievance.model.es.Ticket; import org.upsmf.grievance.dto.SearchRequest; import org.upsmf.grievance.model.reponse.TicketResponse; -import java.util.Optional; +import java.util.Map; public interface SearchService { TicketResponse search(SearchRequest searchRequest); + Map<String, Object> searchTickets(SearchRequest searchRequest); } \ No newline at end of file diff --git a/src/main/java/org/upsmf/grievance/service/impl/SearchServiceImpl.java b/src/main/java/org/upsmf/grievance/service/impl/SearchServiceImpl.java index 4f23203b7b51f2588fdb8bee1b35977a06f5642d..617935e08cc6c0398734aa431f6f4a5246a2b47c 100644 --- a/src/main/java/org/upsmf/grievance/service/impl/SearchServiceImpl.java +++ b/src/main/java/org/upsmf/grievance/service/impl/SearchServiceImpl.java @@ -1,5 +1,15 @@ package org.upsmf.grievance.service.impl; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.action.search.SearchType; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.MatchQueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchHits; +import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.sort.SortOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; @@ -7,12 +17,19 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import org.upsmf.grievance.config.EsConfig; import org.upsmf.grievance.dto.SearchRequest; import org.upsmf.grievance.model.es.Ticket; import org.upsmf.grievance.model.reponse.TicketResponse; import org.upsmf.grievance.repository.es.TicketRepository; import org.upsmf.grievance.service.SearchService; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + @Service public class SearchServiceImpl implements SearchService { @@ -20,6 +37,8 @@ public class SearchServiceImpl implements SearchService { private int defaultPageSize; @Autowired private TicketRepository esTicketRepository; + @Autowired + private EsConfig esConfig; @Override public TicketResponse search(SearchRequest searchRequest) { @@ -29,5 +48,87 @@ public class SearchServiceImpl implements SearchService { Page<Ticket> page = esTicketRepository.findAll(pageable); return TicketResponse.builder().count(page.getTotalElements()).data(page.getContent()).build(); } + @Override + public Map<String, Object> searchTickets(SearchRequest searchRequest) { + //Create query for search by keyword + SearchResponse searchResponse = null; + String keyValue = searchRequest.getSort().keySet().iterator().next(); + if(searchRequest.getSearchKeyword()!=null && !searchRequest.getSearchKeyword().isBlank()){ + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder() + .query(createTicketSearchQuery(searchRequest)) + .from(searchRequest.getPage()) + .size(searchRequest.getSize()) + .sort(keyValue, SortOrder.valueOf(searchRequest.getSort().get(keyValue).toUpperCase())); + + org.elasticsearch.action.search.SearchRequest search = new org.elasticsearch.action.search.SearchRequest("ticket"); + search.searchType(SearchType.QUERY_THEN_FETCH); + search.source(searchSourceBuilder); + try { + searchResponse = esConfig.elasticsearchClient().search(search, RequestOptions.DEFAULT); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + Map<String, Object> response = new HashMap<>(); + List<Object> results = getDocumentsFromSearchResult(searchResponse); + response.put("count", searchResponse.getHits().getTotalHits().value); + response.put("results", results); + return response; + } + + private static List<Object> getDocumentsFromSearchResult(SearchResponse result) { + SearchHits hits = result.getHits(); + return getDocumentsFromHits(hits); + } + private static List<Object> getDocumentsFromHits(SearchHits hits) { + List<Object> documents = new ArrayList<Object>(); + for (SearchHit hit : hits) { + documents.add(hit.getSourceAsMap()); + } + return documents; + } + private BoolQueryBuilder createTicketSearchQuery(SearchRequest searchRequest) { + BoolQueryBuilder finalQuery = QueryBuilders.boolQuery(); + // search by keyword + if(searchRequest.getSearchKeyword() != null && !searchRequest.getSearchKeyword().isBlank()) { + MatchQueryBuilder firstNameKeywordMatchQuery = QueryBuilders.matchQuery("requester_first_name", searchRequest.getSearchKeyword()); + MatchQueryBuilder phoneKeywordMatchQuery = QueryBuilders.matchQuery("requester_phone", searchRequest.getSearchKeyword()); + MatchQueryBuilder emailKeywordMatchQuery = QueryBuilders.matchQuery("requester_email", searchRequest.getSearchKeyword()); + BoolQueryBuilder keywordSearchQuery = QueryBuilders.boolQuery(); + keywordSearchQuery.should(firstNameKeywordMatchQuery).should(phoneKeywordMatchQuery).should(emailKeywordMatchQuery); + finalQuery.must(keywordSearchQuery); + } + if(searchRequest.getCc() != null && !searchRequest.getCc().isBlank()) { + MatchQueryBuilder ccMatchQuery = QueryBuilders.matchQuery("assigned_to_id", searchRequest.getCc()); + BoolQueryBuilder ccSearchQuery = QueryBuilders.boolQuery(); + ccSearchQuery.must(ccMatchQuery); + finalQuery.must(ccSearchQuery); + } + if(searchRequest.getStatus() != null && !searchRequest.getStatus().isEmpty()) { + MatchQueryBuilder statusMatchQuery = QueryBuilders.matchQuery("status", searchRequest.getStatus()); + BoolQueryBuilder statusSearchQuery = QueryBuilders.boolQuery(); + statusSearchQuery.must(statusMatchQuery); + finalQuery.must(statusSearchQuery); + } + if(searchRequest.getIsJunk() != null) { + MatchQueryBuilder junkMatchQuery = QueryBuilders.matchQuery("is_junk", searchRequest.getStatus()); + BoolQueryBuilder junkSearchQuery = QueryBuilders.boolQuery(); + junkSearchQuery.must(junkMatchQuery); + finalQuery.must(junkSearchQuery); + } + if(searchRequest.getPriority() != null) { + MatchQueryBuilder priorityMatchQuery = QueryBuilders.matchQuery("priority", searchRequest.getStatus()); + BoolQueryBuilder prioritySearchQuery = QueryBuilders.boolQuery(); + prioritySearchQuery.must(priorityMatchQuery); + finalQuery.must(prioritySearchQuery); + } + if(searchRequest.getIsEscalated() != null) { + MatchQueryBuilder esclatedMatchQuery = QueryBuilders.matchQuery("is_escalated", searchRequest.getStatus()); + BoolQueryBuilder esclatedSearchQuery = QueryBuilders.boolQuery(); + esclatedSearchQuery.must(esclatedMatchQuery); + finalQuery.must(esclatedSearchQuery); + } + return finalQuery; + } } \ No newline at end of file