Skip to content

Commit

Permalink
avniproject/avni-product#1670 merging after release of 10.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
vedfordev committed Dec 3, 2024
2 parents fad702c + 0d83fb0 commit de79b57
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,17 @@
public class BaseSubjectSearchQueryBuilder<T> {
private final Logger logger;

protected static final String ENCOUNTER_JOIN = "left outer join encounter e\n" +
" on i.id = e.individual_id and\n" +
" e.encounter_date_time is not null and\n" +
" e.is_voided is false";
protected static final String PROGRAM_ENROLMENT_JOIN = "left outer join program_enrolment penr on i.id = penr.individual_id and penr.is_voided is false";
protected static final String PROGRAM_ENCOUNTER_JOIN = "left outer join program_encounter pe\n" +
" on penr.id = pe.program_enrolment_id and\n" +
" pe.encounter_date_time is not null and\n" +
" pe.is_voided is false";
private static final String ADDRESS_LEVEL_JOIN = "left outer join address_level al on al.id = i.address_id";
protected static final String ENCOUNTER_FILTER = "i.id in (select e.individual_id from encounter e where e.encounter_date_time is not null and e.is_voided is false\n";
protected static final String PROGRAM_ENROLMENT_FILTER = "i.id in (select penr.individual_id from program_enrolment penr where penr.enrolment_date_time is not null and penr.is_voided is false\n";
protected static final String PROGRAM_ENCOUNTER_FILTER = "i.id in (select penr.individual_id from program_enrolment penr join program_encounter penc on penc.program_enrolment_id = penr.id where penc.encounter_date_time is not null and penc.is_voided is false and penr.is_voided is false\n";
protected static final String ADDRESS_FILTER = "i.address_id in (select al.id from address_level al where 1=1\n";
protected static final String SEARCH_ALL_FILTER = "i.id in (select i.id from individual i join program_enrolment penr on penr.individual_id = i.id and penr.is_voided is false\n";

private String orderByClause = "";

protected final Set<String> whereClauses = new HashSet<>();
private final Set<String> joinClauses = new LinkedHashSet<>();
private final Map<String, Object> parameters = new HashMap<>();
private boolean forCount;
private boolean findDistinctOnly;
private final Set<String> customFields = new HashSet<>();

public BaseSubjectSearchQueryBuilder() {
Expand All @@ -49,9 +43,8 @@ public BaseSubjectSearchQueryBuilder() {
public SqlQuery buildUsingBaseQuery(String baseQuery, String groupByClause) {
StringBuilder query = new StringBuilder();
query.append(baseQuery);
query.append(String.join(" \n ", joinClauses));

query.append(String.format("\n where i.organisation_id = %d and \n", UserContextHolder.getOrganisation().getId()));
query.append(String.format("\n where i.organisation_id = %d and\n", UserContextHolder.getOrganisation().getId()));

query.append(String.join(" \nand ", whereClauses));
query.append(groupByClause);
Expand All @@ -72,7 +65,7 @@ public SqlQuery buildUsingBaseQuery(String baseQuery, String groupByClause) {
.toString();
}
String customFieldString = customFields.isEmpty() ? "" : ",\n".concat(String.join(",\n", customFields));
String queryWithCustomFields = finalQuery.replace(" $CUSTOM_FIELDS", customFieldString).replace("$DISTINCT", findDistinctOnly ? "distinct" : "");
String queryWithCustomFields = finalQuery.replace(" $CUSTOM_FIELDS", customFieldString);
logger.trace(parameters.toString());
return new SqlQuery(queryWithCustomFields, parameters);
}
Expand Down Expand Up @@ -199,7 +192,7 @@ public T withRegistrationDateFilter(DateRange registrationDateRange) {
return withRangeFilter(registrationDateRange,
"registrationDate",
"i.registration_date >= cast(:rangeParam as date)",
"i.registration_date <= cast(:rangeParam as date)");
"i.registration_date <= cast(:rangeParam as date)", "");
}

public T withIncludeVoidedFilter(Boolean includeVoided) {
Expand All @@ -212,34 +205,50 @@ public T withIncludeVoidedFilter(Boolean includeVoided) {

public T withAddressIdsFilter(List<Integer> addressIds) {
if (addressIds == null || addressIds.isEmpty()) return (T) this;
joinClauses.add(ADDRESS_LEVEL_JOIN);
String addressIdString = addressIds.stream().map(String::valueOf)
.collect(Collectors.joining("|"));
parameters.put("addressLQuery", String.format("*.%s.*", addressIdString));
whereClauses.add("al.lineage ~ cast(:addressLQuery as lquery)");
whereClauses.add(generateWhereClause(ADDRESS_FILTER, "al.lineage ~ cast(:addressLQuery as lquery)"));
return (T) this;
}

public T withSearchAll(String searchString) {
if (StringUtils.isEmpty(searchString)) return (T) this;
String searchValue = "%" + searchString + "%";
parameters.put("searchAll", searchValue);
whereClauses.add("(cast(i.observations as text) ilike :searchAll\n" +
" or cast(penr.observations as text) ilike :searchAll)");
joinClauses.add(PROGRAM_ENROLMENT_JOIN);
this.findDistinctOnly = true;
whereClauses.add(generateWhereClause(SEARCH_ALL_FILTER, "(cast(i.observations as text) ilike :searchAll or cast(penr.observations as text) ilike :searchAll)"));
return (T) this;
}

private String getFilterForSearchScope(String searchScope) {
if (searchScope.equalsIgnoreCase("encounter")) {
return ENCOUNTER_FILTER;
}
if (searchScope.equalsIgnoreCase("programEnrolment")) {
return PROGRAM_ENROLMENT_FILTER;
}
if (searchScope.equalsIgnoreCase("programEncounter")) {
return PROGRAM_ENCOUNTER_FILTER;
}
return "";
}

protected String generateWhereClause(String filterWithDefaultConditions, String moreConditions) {
if (!StringUtils.isEmpty(filterWithDefaultConditions)) {
return filterWithDefaultConditions + " and " + moreConditions + ")";
} else {
return moreConditions;
}
}

public T withConceptsFilter(List<Concept> concept) {
if (concept == null || concept.isEmpty()) return (T) this;
Map<String, String> aliasMap = new HashMap<String, String>() {
{
put("REGISTRATION", "i");
put("PROGRAMENROLMENT", "penr");
put("PROGRAMENCOUNTER", "pe");
put("PROGRAMENCOUNTER", "penc");
put("ENCOUNTER", "e");

}
};

Expand All @@ -248,16 +257,6 @@ public T withConceptsFilter(List<Concept> concept) {
String conceptUuidParam = "conceptUuid" + ci;
addParameter(conceptUuidParam, c.getUuid());
String tableAlias = aliasMap.get(c.getSearchScope().toUpperCase());
if (c.getSearchScope().equalsIgnoreCase("encounter")) {
withJoin(ENCOUNTER_JOIN, true);
}
if (c.getSearchScope().equalsIgnoreCase("programEnrolment")) {
withJoin(PROGRAM_ENROLMENT_JOIN, true);
}
if (c.getSearchScope().equalsIgnoreCase("programEncounter")) {
withJoin(PROGRAM_ENROLMENT_JOIN, true);
withJoin(PROGRAM_ENCOUNTER_JOIN, true);
}

if (c.getDataType().equalsIgnoreCase("CODED")) {
List<String> conditions = new ArrayList<>();
Expand All @@ -273,14 +272,14 @@ public T withConceptsFilter(List<Concept> concept) {
conditions.add(sql);
}
String codedFilter = String.join(" or ", conditions);
whereClauses.add("(" + codedFilter + ")");
whereClauses.add(generateWhereClause(getFilterForSearchScope(c.getSearchScope()), "(" + codedFilter + ")"));
}

if (c.getDataType().equalsIgnoreCase("TEXT") || c.getDataType().equalsIgnoreCase("ID")) {
String value = "%" + c.getValue() + "%";
String param = "textValue" + ci;
addParameter(param, value);
whereClauses.add(tableAlias + ".observations->> :" + conceptUuidParam + " ilike :" + param);
whereClauses.add(generateWhereClause(getFilterForSearchScope(c.getSearchScope()), tableAlias + ".observations->> :" + conceptUuidParam + " ilike :" + param));
}

if (c.getDataType().equalsIgnoreCase("NUMERIC")) {
Expand All @@ -289,19 +288,19 @@ public T withConceptsFilter(List<Concept> concept) {
Float value = Float.parseFloat(c.getMinValue());
String param = "numericValueMin" + ci;
addParameter(param, value);
whereClauses.add("cast(" + tableAlias + ".observations ->> :" + conceptUuidParam + " as numeric) >= :" + param);
whereClauses.add(generateWhereClause(getFilterForSearchScope(c.getSearchScope()),"cast(" + tableAlias + ".observations ->> :" + conceptUuidParam + " as numeric) >= :" + param));
}
if (!StringUtils.isEmpty(c.getMaxValue())) {
Float value = Float.parseFloat(c.getMaxValue());
String param = "numericValueMax" + ci;
addParameter(param, value);
whereClauses.add("cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as numeric) <= :" + param);
whereClauses.add(generateWhereClause(getFilterForSearchScope(c.getSearchScope()),"cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as numeric) <= :" + param));
}
} else {
Float value = Float.parseFloat(c.getMinValue());
String param = "numericValueMin" + ci;
addParameter(param, value);
whereClauses.add("cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as numeric) = :" + param);
whereClauses.add(generateWhereClause(getFilterForSearchScope(c.getSearchScope()),"cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as numeric) = :" + param));
}
}

Expand All @@ -311,43 +310,37 @@ public T withConceptsFilter(List<Concept> concept) {
String value = c.getMinValue();
String param = "dateTimeValueMin" + ci;
addParameter(param, value);
whereClauses.add("cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as date) >= cast(:" + param + " as date)");
whereClauses.add(generateWhereClause(getFilterForSearchScope(c.getSearchScope()),"cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as date) >= cast(:" + param + " as date)"));
}
if (!StringUtils.isEmpty(c.getMaxValue())) {
String value = c.getMaxValue();
String param = "dateTimeValueMax" + ci;
addParameter(param, value);
whereClauses.add("cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as date) <= cast(:" + param + " as date)");
whereClauses.add(generateWhereClause(getFilterForSearchScope(c.getSearchScope()),"cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as date) <= cast(:" + param + " as date)"));
}
} else {
String value = c.getMinValue();
String param = "dateTimeValueMin" + ci;
addParameter(param, value);
whereClauses.add("cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as date) = cast(:" + param + " as date)");
whereClauses.add(generateWhereClause(getFilterForSearchScope(c.getSearchScope()),"cast(" + tableAlias + ".observations ->>:" + conceptUuidParam + " as date) = cast(:" + param + " as date)"));
}
}
}

return (T) this;
}

protected T withJoin(String joinClause, boolean findDistinctOnly) {
joinClauses.add(joinClause);
this.findDistinctOnly = findDistinctOnly || this.findDistinctOnly;
return (T) this;
}

protected T withRangeFilter(RangeFilter rangeFilter, String parameterPrefix, String minFilter, String maxFilter) {
protected T withRangeFilter(RangeFilter rangeFilter, String parameterPrefix, String minFilter, String maxFilter, String filter) {
if (rangeFilter == null) return (T) this;
if (rangeFilter.getMinValue() != null) {
String parameter = parameterPrefix + "Min";
addParameter(parameter, rangeFilter.getMinValue());
whereClauses.add(minFilter.replace("rangeParam", parameter));
whereClauses.add(generateWhereClause(filter, minFilter.replace("rangeParam", parameter)));
}
if (rangeFilter.getMaxValue() != null) {
String parameter = parameterPrefix + "Max";
addParameter(parameter, rangeFilter.getMaxValue());
whereClauses.add(maxFilter.replace("rangeParam", parameter));
whereClauses.add(generateWhereClause(filter, maxFilter.replace("rangeParam", parameter)));
}
return (T) this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class SubjectSearchQueryBuilder extends BaseSubjectSearchQueryBuilder<Sub
public static final String SubjectTypeJoin = " left outer join subject_type st on i.subject_type_id = st.id and st.is_voided is false\n";

public SqlQuery build(SubjectType subjectType) {
String baseQuery = "select $DISTINCT i.id as \"id\",\n" +
String baseQuery = "select i.id as \"id\",\n" +
" i.first_name as \"firstName\",\n" +
" i.last_name as \"lastName\",\n" +
" i.profile_picture as \"profilePicture\",\n" +
Expand All @@ -39,6 +39,8 @@ public SqlQuery build(SubjectType subjectType) {
public SubjectSearchQueryBuilder withSubjectSearchFilter(SubjectSearchRequest request, SubjectType subjectType) {
return this
.withNameFilter(request.getName())
.withConceptsFilter(request.getConcept())
.withSearchAll(request.getSearchAll())
.withSubjectTypeFilter(subjectType)
.withGenderFilter(request.getGender())
.withAgeFilter(request.getAge())
Expand All @@ -47,9 +49,7 @@ public SubjectSearchQueryBuilder withSubjectSearchFilter(SubjectSearchRequest re
.withProgramEnrolmentDateFilter(request.getProgramEnrolmentDate())
.withProgramEncounterDateFilter(request.getProgramEncounterDate())
.withAddressIdsFilter(request.getAddressIds())
.withConceptsFilter(request.getConcept())
.withIncludeVoidedFilter(request.getIncludeVoided())
.withSearchAll(request.getSearchAll())
.withPaginationFilters(request.getPageElement())
.withCustomFields(request.getSubjectType());
}
Expand All @@ -63,30 +63,26 @@ public SubjectSearchQueryBuilder withSubjectTypeFilter(SubjectType subjectType)

public SubjectSearchQueryBuilder withEncounterDateFilter(DateRange encounterDateRange) {
if (encounterDateRange == null || encounterDateRange.isEmpty()) return this;
return withJoin(ENCOUNTER_JOIN, true)
.withRangeFilter(encounterDateRange,
return withRangeFilter(encounterDateRange,
"encounterDate",
"e.encounter_date_time >= cast(:rangeParam as date)",
"e.encounter_date_time <= cast(:rangeParam as date)");
"e.encounter_date_time <= cast(:rangeParam as date)", ENCOUNTER_FILTER);
}

public SubjectSearchQueryBuilder withProgramEncounterDateFilter(DateRange dateRange) {
if (dateRange == null || dateRange.isEmpty()) return this;
return withJoin(PROGRAM_ENROLMENT_JOIN, true)
.withJoin(PROGRAM_ENCOUNTER_JOIN, true)
.withRangeFilter(dateRange,
return withRangeFilter(dateRange,
"programEncounterDate",
"pe.encounter_date_time >= cast(:rangeParam as date)",
"pe.encounter_date_time <= cast(:rangeParam as date)");
"pe.encounter_date_time <= cast(:rangeParam as date)", PROGRAM_ENCOUNTER_FILTER);
}

public SubjectSearchQueryBuilder withProgramEnrolmentDateFilter(DateRange dateRange) {
if (dateRange == null || dateRange.isEmpty()) return this;
return withJoin(PROGRAM_ENROLMENT_JOIN, true)
.withRangeFilter(dateRange,
return withRangeFilter(dateRange,
"programEnrolmentDate",
"penr.enrolment_date_time >= cast(trim(:rangeParam) as date)",
"penr.enrolment_date_time <= cast(trim(:rangeParam) as date)");
"penr.enrolment_date_time <= cast(trim(:rangeParam) as date)", PROGRAM_ENROLMENT_FILTER);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
import org.avni.server.dao.search.SubjectSearchQueryBuilder;
import org.avni.server.projection.SearchSubjectEnrolledProgram;
import org.avni.server.web.request.webapp.search.SubjectSearchRequest;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.*;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import java.util.stream.Collectors;

Expand All @@ -28,15 +32,15 @@ public IndividualSearchService(SubjectSearchRepository subjectSearchRepository,
}

public LinkedHashMap<String, Object> search(SubjectSearchRequest subjectSearchRequest) {
long startTime = new DateTime().getMillis();
logger.info("Searching for individuals");
List<Map<String, Object>> searchResults = subjectSearchRepository.search(subjectSearchRequest, new SubjectSearchQueryBuilder());
logger.info("Found " + searchResults.size() + " individuals");
BigInteger totalCount = subjectSearchRepository.getTotalCount(subjectSearchRequest, new SubjectSearchQueryBuilder());
logger.info("Total count of individuals: " + totalCount);
return constructIndividual(searchResults, totalCount);
long resultsEnd = new DateTime().getMillis();
logger.info(String.format("Subject search: Time Taken: %dms. Sorted: %s", (resultsEnd - startTime), subjectSearchRequest.getPageElement().getSortColumn()));
return constructIndividual(searchResults);
}

private LinkedHashMap<String, Object> constructIndividual(List<Map<String, Object>> individualList, BigInteger totalCount) {
private LinkedHashMap<String, Object> constructIndividual(List<Map<String, Object>> individualList) {
LinkedHashMap<String, Object> recordsMap = new LinkedHashMap<String, Object>();
List<Long> individualIds = individualList.stream()
.map(individualRecord -> Long.valueOf((Integer) individualRecord.get("id")))
Expand All @@ -60,7 +64,6 @@ private LinkedHashMap<String, Object> constructIndividual(List<Map<String, Objec
.collect(Collectors.toList()));
individualRecord.put("addressLevel", titleLineages.get(((BigInteger) individualRecord.get("addressId")).longValue()));
}).collect(Collectors.toList());
recordsMap.put("totalElements", totalCount);
recordsMap.put("listOfRecords", listOfRecords);
return recordsMap;
}
Expand Down

0 comments on commit de79b57

Please sign in to comment.