Skip to content

Commit

Permalink
avniproject/avni-media#179 | Media search performance improvements
Browse files Browse the repository at this point in the history
- Respect index configuration change on media table
- Use alternate query when media is not filtered based on address to avoid expensive address join for orgs with lot of address levels
  • Loading branch information
1t5j0y committed Oct 23, 2024
1 parent aed6bdd commit b848274
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ public ColumnMetadata(Column column, Integer conceptId, ConceptType conceptType,
this(null, column, conceptId, conceptType, conceptUuid, null);
}

public ColumnMetadata(Column column) {
this(column, null, null, null);
}

public Integer getConceptId() {
return conceptId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private <T> List<T> searchInternal(MediaSearchRequest mediaSearchRequest, Page p
conceptFilterSearches = determineConceptFilterTablesAndColumns(mediaSearchRequest.getConceptFilters());
}

Query query = new MediaSearchQueryBuilder()
Query query = new MediaSearchQueryBuilder(mediaSearchRequest)
.withPage(page)
.withMediaSearchRequest(mediaSearchRequest)
.withSearchConceptFilters(conceptFilterSearches)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.avniproject.etl.repository.rowMappers;

import org.avniproject.etl.domain.metadata.ColumnMetadata;
import org.avniproject.etl.domain.metadata.IndexMetadata;
import org.avniproject.etl.domain.metadata.TableMetadata;
import org.avniproject.etl.repository.rowMappers.tableMappers.MediaTable;

import java.util.Objects;
import java.util.stream.Collectors;

public class MediaTableMetadataBuilder {
Expand All @@ -13,7 +15,7 @@ public static TableMetadata build() {
mediaTableMetadata.setName(mediaTable.name(null));
mediaTableMetadata.setType(TableMetadata.Type.Media);
mediaTableMetadata.addColumnMetadata(mediaTable.columns().stream().map(column -> new ColumnMetadata(column, null, null, null)).collect(Collectors.toList()));

mediaTableMetadata.addIndexMetadata(mediaTable.columns().stream().map(column -> column.isIndexed() ? new IndexMetadata(new ColumnMetadata(column)) : null).filter(Objects::nonNull).collect(Collectors.toList()));
return mediaTableMetadata;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ public class MediaSearchQueryBuilder {
private final ST template;
private final Map<String, Object> parameters = new HashMap<>();
private final static String sqlTemplate = readFile("/sql/api/searchMedia.sql.st");
private final static String withoutAddressFilterSqlTemplate = readFile("/sql/api/searchMediaWithoutAddressFilter.sql.st");
private static final Logger logger = Logger.getLogger(MediaSearchQueryBuilder.class);

public MediaSearchQueryBuilder() {
this.template = new ST(sqlTemplate);
addDefaultParameters();
}

public MediaSearchQueryBuilder(MediaSearchRequest mediaSearchRequest) {
this.template = mediaSearchRequest.getAddresses().isEmpty() ? new ST(withoutAddressFilterSqlTemplate) : new ST(sqlTemplate);
addDefaultParameters();
}

public MediaSearchQueryBuilder withMediaSearchRequest(MediaSearchRequest request) {
template.add("request", request);
addParameters(request);
Expand Down
72 changes: 72 additions & 0 deletions src/main/resources/sql/api/searchMediaWithoutAddressFilter.sql.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
SELECT *, row_to_json(address.*) as address FROM
(
SELECT media.id as id,
media.uuid as uuid,
media.subject_first_name as subject_first_name,
media.subject_last_name as subject_last_name,
media.subject_middle_name as subject_middle_name,
media.is_voided as is_voided,
media.created_by_id as created_by_id,
media.last_modified_by_id as last_modified_by_id,
media.created_date_time as created_date_time,
media.last_modified_date_time as last_modified_date_time,
media.organisation_id as organisation_id,
media.image_url as image_url,
media.sync_parameter_key1 as sync_parameter_key1,
media.sync_parameter_key2 as sync_parameter_key2,
media.sync_parameter_value1 as sync_parameter_value1,
media.sync_parameter_value2 as sync_parameter_value2,
media.subject_type_name as subject_type_name,
media.encounter_type_name as encounter_type_name,
media.program_name as program_name,
media.concept_name as concept_name,
media.address_id as address_id,
media.entity_id as entity_id
FROM <schemaName>.media media
<if(joinTablesAndColumns)>
<joinTablesAndColumns:{joinTableAndColumn |
RIGHT JOIN <schemaName>.<joinTableAndColumn.tableName> <joinTableAndColumn.tableName> ON media.entity_id = <joinTableAndColumn.tableName>.id
<if(joinTableAndColumn.exactSearch)>
AND <joinTableAndColumn.tableName>."<joinTableAndColumn.columnName>"
<if(joinTableAndColumn.columnValues)>
IN ('<joinTableAndColumn.columnValues:{columnValue | <columnValue>}; separator="', '">')
<else>
<! BETWEEN <if(joinTableAndColumn.nonStringValue)>if <joinTableAndColumn.from> AND <joinTableAndColumn.to><else> else '<joinTableAndColumn.from>' AND '<joinTableAndColumn.to>'<endif> !>
<! Below works because postgresql does an implicit type cast to numeric on passing strings for comparing with numeric. Revert to above approach after fixing (currently nonStringValue is always false despite setting boolean) if this causes issues later. !>
BETWEEN '<joinTableAndColumn.from>' AND '<joinTableAndColumn.to>'
<endif>
<else>
AND <joinTableAndColumn.columnValues:{columnValue | <joinTableAndColumn.tableName>."<joinTableAndColumn.columnName>" ILIKE '%<columnValue>%'}; separator=" \nAND ">
<endif>
}; separator="\n">
<endif>
where media.image_url is not null
and media.is_voided is false
<if(request)>
<if(request.fromDate)> and media.created_date_time >= :fromDate <endif>
<if(request.toDate)> and media.created_date_time \<= :toDate <endif>
<if(request.subjectName)> and (<request.subjectNameTokens : {subjectNameToken |
(media.subject_first_name ilike '%<subjectNameToken>%' or media.subject_middle_name ilike '%<subjectNameToken>%' or media.subject_last_name ilike '%<subjectNameToken>%')
}; separator="\n AND ">)
<endif>
<if(request.subjectTypeNames)> and media.subject_type_name in (:subjectTypeNames) <endif>
<if(request.programNames)> and media.program_name in (:programNames) <endif>
<if(request.encounterTypeNames)> and media.encounter_type_name in (:encounterTypeNames) <endif>
<if(request.imageConcepts)> and media.concept_name in (:imageConcepts) <endif>
<if(request.syncValues)> and (<endif>
<request.syncValues:{syncValue|
((media.sync_parameter_key1 = :syncConceptName_<i0> and media.sync_parameter_value1 in (:syncConceptValues_<i0>))
or (media.sync_parameter_key2 = :syncConceptName_<i0> and media.sync_parameter_value2 in (:syncConceptValues_<i0>)))};
separator="\n OR "
>
<if(request.syncValues)> )<endif>
<if(request.addresses)> and (<endif>
<request.addresses:{addressRequest|<if(addressRequest.addressLevelIds)>address."<addressRequest.addressLevelType> id" in (:addressLevelIds_<i0>)<else>1 = 2<endif>}; separator="\n OR ">
<if(request.addresses)> )<endif>
<endif>
ORDER BY media.created_date_time desc
LIMIT :limit OFFSET :offset
) media_search_result
JOIN <schemaName>.address address ON address.id = media_search_result.address_id
ORDER BY media_search_result.created_date_time desc
;

0 comments on commit b848274

Please sign in to comment.