diff --git a/backend/project/src/main/java/org/egov/project/repository/ProjectRepository.java b/backend/project/src/main/java/org/egov/project/repository/ProjectRepository.java index ae7b4c9994..54389508b3 100644 --- a/backend/project/src/main/java/org/egov/project/repository/ProjectRepository.java +++ b/backend/project/src/main/java/org/egov/project/repository/ProjectRepository.java @@ -367,4 +367,38 @@ public Integer getProjectCount(ProjectSearch projectSearch, ProjectSearchURLPara log.info("Total project count is : " + count); return count; } + + public List getProjectsForBulkSearch(String tenantId, Integer limit, Integer offset) { + List preparedStmtList = new ArrayList<>(); + String query = queryBuilder.getProjectQueryForBulkSearch(tenantId, limit, offset, preparedStmtList); + List projects = jdbcTemplate.query(query, addressRowMapper, preparedStmtList.toArray()); + + Set projectIds = projects.stream().map(Project :: getId).collect(Collectors.toSet()); + + List ancestors = null; + List descendants = null; + //Get Project ancestors if includeAncestors flag is true + ancestors = getProjectAncestors(projects); + if (ancestors != null && !ancestors.isEmpty()) { + List ancestorProjectIds = ancestors.stream().map(Project :: getId).collect(Collectors.toList()); + projectIds.addAll(ancestorProjectIds); + } + + //Get Project descendants if includeDescendants flag is true + descendants = getProjectDescendants(projects); + if (descendants != null && !descendants.isEmpty()) { + List descendantsProjectIds = descendants.stream().map(Project :: getId).collect(Collectors.toList()); + projectIds.addAll(descendantsProjectIds); + } + + //Fetch targets based on Project Ids + List targets = getTargetsBasedOnProjectIds(projectIds); + + //Fetch documents based on Project Ids + List documents = getDocumentsBasedOnProjectIds(projectIds); + + //Construct Project Objects with fetched projects, targets and documents using Project id + return buildProjectSearchResult(projects, targets, documents, ancestors, descendants); + } + } \ No newline at end of file diff --git a/backend/project/src/main/java/org/egov/project/repository/querybuilder/ProjectAddressQueryBuilder.java b/backend/project/src/main/java/org/egov/project/repository/querybuilder/ProjectAddressQueryBuilder.java index 466c3549af..3b30a26a91 100644 --- a/backend/project/src/main/java/org/egov/project/repository/querybuilder/ProjectAddressQueryBuilder.java +++ b/backend/project/src/main/java/org/egov/project/repository/querybuilder/ProjectAddressQueryBuilder.java @@ -377,4 +377,28 @@ public String getSearchCountQueryString(ProjectSearch projectSearch, ProjectSear String query = getProjectSearchQuery(projectSearch, urlParams, preparedStatement, Boolean.TRUE); return query; } + + public String getProjectQueryForBulkSearch(String tenantId, Integer limit, Integer offset, List preparedStmtList) { + + StringBuilder queryBuilder = new StringBuilder(FETCH_PROJECT_ADDRESS_QUERY); + + // Check if tenant ID is provided in URL parameters + if (StringUtils.isNotBlank(tenantId)) { + addClauseIfRequired(preparedStmtList, queryBuilder); + if (!tenantId.contains(DOT)) { + // State level tenant ID: use LIKE for partial matching + log.info("State level tenant"); + queryBuilder.append(" prj.tenantId like ? "); + preparedStmtList.add(tenantId + '%'); + } else { + // City level tenant ID: use exact match + log.info("City level tenant"); + queryBuilder.append(" prj.tenantId=? "); + preparedStmtList.add(tenantId); + } + } + + // Wrap constructed SQL query with pagination criteria + return addPaginationWrapper(queryBuilder.toString(), preparedStmtList, limit, offset); + } } diff --git a/backend/project/src/main/java/org/egov/project/service/ProjectService.java b/backend/project/src/main/java/org/egov/project/service/ProjectService.java index 4b972e35fd..3dc8468fd6 100644 --- a/backend/project/src/main/java/org/egov/project/service/ProjectService.java +++ b/backend/project/src/main/java/org/egov/project/service/ProjectService.java @@ -340,4 +340,13 @@ public Integer countAllProjects(ProjectRequest project, String tenantId, Long la public Integer countAllProjects(ProjectSearchRequest projectSearchRequest, ProjectSearchURLParams urlParams) { return projectRepository.getProjectCount(projectSearchRequest.getProject(), urlParams); } + + public List plainsearchProject(String tenantId, Integer limit, Integer offset) { + List projects = projectRepository.getProjectsForBulkSearch(tenantId, limit, offset); + + // enrichment if needed + + return projects; + } + } diff --git a/backend/project/src/main/java/org/egov/project/util/BoundaryUtil.java b/backend/project/src/main/java/org/egov/project/util/BoundaryUtil.java index 3a0a08d31e..fa418839fe 100644 --- a/backend/project/src/main/java/org/egov/project/util/BoundaryUtil.java +++ b/backend/project/src/main/java/org/egov/project/util/BoundaryUtil.java @@ -1,95 +1,95 @@ -package org.egov.project.util; - -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; -import digit.models.coremodels.RequestInfoWrapper; -import lombok.extern.slf4j.Slf4j; -import net.minidev.json.JSONObject; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.egov.common.contract.request.RequestInfo; -import org.egov.common.http.client.ServiceRequestClient; -import org.egov.tracer.model.CustomException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - - -@Component -@Slf4j -public class BoundaryUtil { - - @Value("${egov.location.host}") - private String locationHost; - - @Value("${egov.location.context.path}") - private String locationContextPath; - - @Value("${egov.location.endpoint}") - private String locationEndpoint; - - @Value("${egov.location.code.query.param:codes}") - private String codeQueryParam; - - @Autowired - private ServiceRequestClient serviceRequestRepository; - - /* Takes map of locations with boundaryType as key and list of boundaries as its values, tenantId, requestInfo and hierarchyType. - * For each boundaryType, egov-location service is called with hierarchyType, tenantId and codes as parameters. The boundaries in request are validated against the egov-location response*/ - public void validateBoundaryDetails(Map> locations, String tenantId, RequestInfo requestInfo, String hierarchyTypeCode) { - for (Map.Entry> entry : locations.entrySet()) { - String boundaryType = entry.getKey(); - List boundaries = entry.getValue(); - - log.info("Validating boundary for boundary type " + boundaryType + " with hierarchyType " + hierarchyTypeCode); - StringBuilder uri = new StringBuilder(locationHost); - uri.append(locationContextPath).append(locationEndpoint); - uri.append("?").append("tenantId=").append(tenantId); - - if (hierarchyTypeCode != null) - uri.append("&").append("hierarchyTypeCode=").append(hierarchyTypeCode); - - uri.append("&").append("boundaryType=").append(boundaryType).append("&") - .append(String.format("%s=", codeQueryParam)) - .append(StringUtils.join(boundaries, ',')); - - Optional response = null; - try { - response = Optional.ofNullable(serviceRequestRepository.fetchResult(uri, - RequestInfoWrapper.builder().requestInfo(requestInfo).build(), LinkedHashMap.class)); - } catch (Exception e) { - log.error("error while calling boundary service", ExceptionUtils.getStackTrace(e)); - throw new CustomException("BOUNDARY_ERROR", "error while calling boundary service"); - } - - if (response.isPresent()) { - LinkedHashMap responseMap = (LinkedHashMap) response.get(); - if (CollectionUtils.isEmpty(responseMap)) - throw new CustomException("BOUNDARY ERROR", "The response from location service is empty or null"); - String jsonString = new JSONObject(responseMap).toString(); - - for (String boundary : boundaries) { - String jsonpath = "$..boundary[?(@.code==\"{}\")]"; - jsonpath = jsonpath.replace("{}", boundary); - DocumentContext context = JsonPath.parse(jsonString); - Object boundaryObject = context.read(jsonpath); - - if (!(boundaryObject instanceof ArrayList) || CollectionUtils.isEmpty((ArrayList) boundaryObject)) { - log.error("The boundary data for the code " + boundary + " is not available"); - throw new CustomException("INVALID_BOUNDARY_DATA", "The boundary data for the code " - + boundary + " is not available"); - } - } - } - log.info("The boundaries " + StringUtils.join(boundaries, ',') + " validated for boundary type " + boundaryType + " with tenantId " + tenantId); - } - } - -} \ No newline at end of file +//package org.egov.project.util; +// +//import com.jayway.jsonpath.DocumentContext; +//import com.jayway.jsonpath.JsonPath; +//import digit.models.coremodels.RequestInfoWrapper; +//import lombok.extern.slf4j.Slf4j; +//import net.minidev.json.JSONObject; +//import org.apache.commons.lang3.StringUtils; +//import org.apache.commons.lang3.exception.ExceptionUtils; +//import org.egov.common.contract.request.RequestInfo; +//import org.egov.common.http.client.ServiceRequestClient; +//import org.egov.tracer.model.CustomException; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.stereotype.Component; +//import org.springframework.util.CollectionUtils; +// +//import java.util.ArrayList; +//import java.util.LinkedHashMap; +//import java.util.List; +//import java.util.Map; +//import java.util.Optional; +// +// +//@Component +//@Slf4j +//public class BoundaryUtil { +// +// @Value("${egov.location.host}") +// private String locationHost; +// +// @Value("${egov.location.context.path}") +// private String locationContextPath; +// +// @Value("${egov.location.endpoint}") +// private String locationEndpoint; +// +// @Value("${egov.location.code.query.param:codes}") +// private String codeQueryParam; +// +// @Autowired +// private ServiceRequestClient serviceRequestRepository; +// +// /* Takes map of locations with boundaryType as key and list of boundaries as its values, tenantId, requestInfo and hierarchyType. +// * For each boundaryType, egov-location service is called with hierarchyType, tenantId and codes as parameters. The boundaries in request are validated against the egov-location response*/ +// public void validateBoundaryDetails(Map> locations, String tenantId, RequestInfo requestInfo, String hierarchyTypeCode) { +// for (Map.Entry> entry : locations.entrySet()) { +// String boundaryType = entry.getKey(); +// List boundaries = entry.getValue(); +// +// log.info("Validating boundary for boundary type " + boundaryType + " with hierarchyType " + hierarchyTypeCode); +// StringBuilder uri = new StringBuilder(locationHost); +// uri.append(locationContextPath).append(locationEndpoint); +// uri.append("?").append("tenantId=").append(tenantId); +// +// if (hierarchyTypeCode != null) +// uri.append("&").append("hierarchyTypeCode=").append(hierarchyTypeCode); +// +// uri.append("&").append("boundaryType=").append(boundaryType).append("&") +// .append(String.format("%s=", codeQueryParam)) +// .append(StringUtils.join(boundaries, ',')); +// +// Optional response = null; +// try { +// response = Optional.ofNullable(serviceRequestRepository.fetchResult(uri, +// RequestInfoWrapper.builder().requestInfo(requestInfo).build(), LinkedHashMap.class)); +// } catch (Exception e) { +// log.error("error while calling boundary service", ExceptionUtils.getStackTrace(e)); +// throw new CustomException("BOUNDARY_ERROR", "error while calling boundary service"); +// } +// +// if (response.isPresent()) { +// LinkedHashMap responseMap = (LinkedHashMap) response.get(); +// if (CollectionUtils.isEmpty(responseMap)) +// throw new CustomException("BOUNDARY ERROR", "The response from location service is empty or null"); +// String jsonString = new JSONObject(responseMap).toString(); +// +// for (String boundary : boundaries) { +// String jsonpath = "$..boundary[?(@.code==\"{}\")]"; +// jsonpath = jsonpath.replace("{}", boundary); +// DocumentContext context = JsonPath.parse(jsonString); +// Object boundaryObject = context.read(jsonpath); +// +// if (!(boundaryObject instanceof ArrayList) || CollectionUtils.isEmpty((ArrayList) boundaryObject)) { +// log.error("The boundary data for the code " + boundary + " is not available"); +// throw new CustomException("INVALID_BOUNDARY_DATA", "The boundary data for the code " +// + boundary + " is not available"); +// } +// } +// } +// log.info("The boundaries " + StringUtils.join(boundaries, ',') + " validated for boundary type " + boundaryType + " with tenantId " + tenantId); +// } +// } +// +//} \ No newline at end of file diff --git a/backend/project/src/main/java/org/egov/project/web/controllers/ProjectApiController.java b/backend/project/src/main/java/org/egov/project/web/controllers/ProjectApiController.java index 1b74b7ab1d..987f482cdb 100644 --- a/backend/project/src/main/java/org/egov/project/web/controllers/ProjectApiController.java +++ b/backend/project/src/main/java/org/egov/project/web/controllers/ProjectApiController.java @@ -10,6 +10,7 @@ import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; +import org.egov.common.contract.models.RequestInfoWrapper; import org.egov.common.contract.response.ResponseInfo; import org.egov.common.models.core.ProjectSearchURLParams; import org.egov.common.models.core.SearchResponse; @@ -529,4 +530,20 @@ public ResponseEntity updateProject(@ApiParam(value = "Details return new ResponseEntity(projectResponse, HttpStatus.OK); } + @RequestMapping(value = "/v1/_plainsearch", method = RequestMethod.POST) + public ResponseEntity plainsearchV1Project( + @Valid @RequestBody RequestInfoWrapper requestInfoWrapper, + @ApiParam(value = "Tenant id", defaultValue = "false") @Valid @RequestParam(value = "TenantId", required = false) String tenantId, + @ApiParam(value = "Limit", defaultValue = "false") @Valid @RequestParam(value = "limit", required = true) Integer limit, + @ApiParam(value = "Offset", defaultValue = "false") @Valid @RequestParam(value = "offset", required = true) Integer offset + ) { + List projects = projectService.plainsearchProject(tenantId, limit, offset); + ResponseInfo responseInfo = ResponseInfoFactory.createResponseInfo(requestInfoWrapper.getRequestInfo(), true); + ProjectResponse projectResponse = ProjectResponse.builder() + .responseInfo(responseInfo) + .project(projects) + .build(); + return new ResponseEntity(projectResponse, HttpStatus.OK); + } + } diff --git a/build/build-config.yml b/build/build-config.yml index 8a43979d46..0cc51a4ecc 100644 --- a/build/build-config.yml +++ b/build/build-config.yml @@ -126,7 +126,15 @@ config: dockerfile: "build/maven-java17/Dockerfile" image-name: "rate-analysis" - work-dir: "backend/rate-analysis/src/main/resources/db" - image-name: "rate-analysis-db" + image-name: "rate-analysis-db" + + - name: "builds/mukta_impl/backend/project" + build: + - work-dir: "backend/project" + image-name: "project" + dockerfile: "build/maven-java17/Dockerfile" + - work-dir: "backend/project/src/main/resources/db" + image-name: "project-db" # utilities - name: "builds/mukta_impl/utilities/egov-pdf"