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

HCMPRE-2212 making facilityIds in plan search a set for multi select dropdown #1360

Merged
merged 7 commits into from
Jan 28, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,11 @@ public class ServiceConstants {

public static final String CONFIRMED_TARGET_POPULATION = "CONFIRMED_HCM_ADMIN_CONSOLE_TARGET_POPULATION";

public static final String ADDITIONAL_DETAILS_QUERY = " additional_details @> CAST( ? AS jsonb )";

public static final String JSONB_QUERY_FORMAT = "additional_details @> ?::jsonb";

public static final String AND_CONDITION = " AND ";

public static final String OR_CONDITION = " OR ";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.*;

import static digit.config.ServiceConstants.ADDITIONAL_DETAILS_QUERY;
import static digit.config.ServiceConstants.FACILITY_ID_SEARCH_PARAMETER_KEY;

@Component
public class PlanQueryBuilder {
Expand Down Expand Up @@ -172,11 +172,21 @@ private String buildPlanSearchQuery(PlanSearchCriteria planSearchCriteria, List<
queryUtil.addToPreparedStatement(preparedStmtList, planSearchCriteria.getJurisdiction());
}

if(!CollectionUtils.isEmpty(planSearchCriteria.getFiltersMap())) {
queryUtil.addClauseIfRequired(builder, preparedStmtList);
builder.append(" additional_details @> CAST( ? AS jsonb )");
String partialQueryJsonString = queryUtil.preparePartialJsonStringFromFilterMap(planSearchCriteria.getFiltersMap());
preparedStmtList.add(partialQueryJsonString);
if (!CollectionUtils.isEmpty(planSearchCriteria.getFiltersMap())) {
Map<String, Set<String>> filtersMap = planSearchCriteria.getFiltersMap();
for (String key : filtersMap.keySet()) {
if (FACILITY_ID_SEARCH_PARAMETER_KEY.equals(key)) {
// its for facility multi select then no need to add to preparedStmtList
String partialQueryJsonString = queryUtil.preparePartialJsonStringFromFilterMap(planSearchCriteria.getFiltersMap(), preparedStmtList, key);
builder.append(partialQueryJsonString);
} else {
// handle all other keys normally.
queryUtil.addClauseIfRequired(builder, preparedStmtList);
builder.append(ADDITIONAL_DETAILS_QUERY);
String partialQueryJsonString = queryUtil.preparePartialJsonStringFromFilterMap(planSearchCriteria.getFiltersMap(), preparedStmtList, key);
preparedStmtList.add(partialQueryJsonString);
}
}
}

StringBuilder countQuery = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,31 +253,31 @@ public void enrichSearchRequest(PlanSearchRequest planSearchRequest) {
PlanSearchCriteria planSearchCriteria = planSearchRequest.getPlanSearchCriteria();

// Filter map for filtering plan metadata present in additional details
Map<String, String> filtersMap = new LinkedHashMap<>();
Map<String, Set<String>> filtersMap = new LinkedHashMap<>();

// Add facility id as a filter if present in search criteria
if (!ObjectUtils.isEmpty(planSearchCriteria.getFacilityId())) {
filtersMap.put(FACILITY_ID_SEARCH_PARAMETER_KEY, planSearchCriteria.getFacilityId());
if (!ObjectUtils.isEmpty(planSearchCriteria.getFacilityIds())) {
filtersMap.put(FACILITY_ID_SEARCH_PARAMETER_KEY, planSearchCriteria.getFacilityIds());
}

// Add terrain as a filter if present in search criteria
if (!ObjectUtils.isEmpty(planSearchCriteria.getTerrain())) {
filtersMap.put(TERRAIN_CONDITION_SEARCH_PARAMETER_KEY, planSearchCriteria.getTerrain());
filtersMap.put(TERRAIN_CONDITION_SEARCH_PARAMETER_KEY, Collections.singleton(planSearchCriteria.getTerrain()));
}

// Add onRoadCondition as a filter if present in search criteria
if (!ObjectUtils.isEmpty(planSearchCriteria.getOnRoadCondition())) {
filtersMap.put(ROAD_CONDITION_SEARCH_PARAMETER_KEY, planSearchCriteria.getOnRoadCondition());
filtersMap.put(ROAD_CONDITION_SEARCH_PARAMETER_KEY, Collections.singleton(planSearchCriteria.getOnRoadCondition()));
}

// Add securityQ1 as a filter if present in search criteria
if (!ObjectUtils.isEmpty(planSearchCriteria.getSecurityQ1())) {
filtersMap.put(SECURITY_Q1_SEARCH_PARAMETER_KEY, planSearchCriteria.getSecurityQ1());
filtersMap.put(SECURITY_Q1_SEARCH_PARAMETER_KEY, Collections.singleton(planSearchCriteria.getSecurityQ1()));
}

// Add securityQ2 as a filter if present in search criteria
if (!ObjectUtils.isEmpty(planSearchCriteria.getSecurityQ2())) {
filtersMap.put(SECURITY_Q2_SEARCH_PARAMETER_KEY, planSearchCriteria.getSecurityQ2());
filtersMap.put(SECURITY_Q2_SEARCH_PARAMETER_KEY, Collections.singleton(planSearchCriteria.getSecurityQ2()));
}

if(!CollectionUtils.isEmpty(filtersMap))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,10 @@
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.IntStream;

import static digit.config.ServiceConstants.DOT_REGEX;
import static digit.config.ServiceConstants.DOT_SEPARATOR;
import static digit.config.ServiceConstants.*;

@Component
public class QueryUtil {
Expand Down Expand Up @@ -99,6 +95,8 @@ public String addOrderByClause(String query, String orderByClause) {
/**
* This method prepares partial json string from the filter map to query on jsonb column
*
* - For nested keys (keys containing dot separators), a nested JSON structure is constructed.
* - For other keys, a simple key-value JSON structure is returned.
* @param filterMap
* @return
*/
Expand All @@ -121,6 +119,63 @@ public String preparePartialJsonStringFromFilterMap(Map<String, String> filterMa
return partialJsonQueryString;
}

/**
* This is an overloaded function, to handle facility multi select for plan search specifically.
* Prepares a partial JSON query string based on the provided filter map, key, and prepared statement list.
*
* @param filterMap A map containing filter keys and their corresponding set of values.
* @param preparedStmtList A list to which placeholder values for prepared statements are added.
* @param key The key used to construct the partial query.
* @return A partial JSON query string or a query fragment with OR conditions.
*
* This method dynamically constructs a JSON query or query fragment based on the structure of the `key`:
* - For nested keys (keys containing dot separators), a nested JSON structure is constructed.
* - For the "facilityId" key, an OR condition is generated for all values in the set.
* - For other keys, a simple key-value JSON structure is returned.
*
* Notes:
* - When constructing the query for "facilityId", placeholders for each value are added to the `preparedStmtList`.
*/
public String preparePartialJsonStringFromFilterMap(Map<String, Set<String>> filterMap, List<Object> preparedStmtList, String key) {
Map<String, Object> queryMap = new HashMap<>();
StringBuilder finalJsonQuery = new StringBuilder();
// Handle nested keys (dot separator)
if (key.contains(DOT_SEPARATOR)) {
String[] keyArray = key.split(DOT_REGEX);
Map<String, Object> nestedQueryMap = new HashMap<>();
prepareNestedQueryMap(0, keyArray, nestedQueryMap, (String) filterMap.get(key).toArray()[0]);
queryMap.put(keyArray[0], nestedQueryMap.get(keyArray[0]));
return gson.toJson(queryMap);
} else if (FACILITY_ID_SEARCH_PARAMETER_KEY.equals(key)) {
Set<String> values = filterMap.get(key);
if (values != null && !values.isEmpty()) {
StringBuilder orClauseBuilder = new StringBuilder();

// For each value, add an OR condition with a placeholder for the value
for (String value : values) {
if (orClauseBuilder.length() > 0) {
orClauseBuilder.append(OR_CONDITION);
}
// Add the condition for the key-value pair
orClauseBuilder.append(JSONB_QUERY_FORMAT);
// Add the actual value in the format { "facilityId": "value" }
preparedStmtList.add("{\"" + key + "\": \"" + value + "\"}");
}

// Append the OR clause as part of the AND conditions
finalJsonQuery.append(AND_CONDITION).append("(").append(orClauseBuilder.toString()).append(") ");
return finalJsonQuery.toString(); // Return the query with OR conditions for facilityId
}
} else {
queryMap.put(key, (String) filterMap.get(key).toArray()[0]);
}


// Return the dynamically constructed query string
return gson.toJson(queryMap);
}


/**
* Tail recursive method to prepare n-level nested partial json for queries on nested data in
* master data. For e.g. , if the key is in the format a.b.c, it will construct a nested json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public class PlanSearchCriteria {
@JsonProperty("planConfigurationId")
private String planConfigurationId = null;

@JsonProperty("facilityId")
private String facilityId = null;
@JsonProperty("facilityIds")
private Set<String> facilityIds = null;

@JsonProperty("onRoadCondition")
private String onRoadCondition = null;
Expand Down Expand Up @@ -72,6 +72,6 @@ public class PlanSearchCriteria {
private Integer limit = null;

@JsonIgnore
private Map<String, String> filtersMap = null;
private Map<String, Set<String>> filtersMap = null;

}
Loading