From 5503404c76f8a96532bbb06fea7c22acf0d80a14 Mon Sep 17 00:00:00 2001 From: Jason Loux Date: Wed, 16 Sep 2026 19:01:41 -0400 Subject: [PATCH] Changes to support filtering/sorting for germplasm - Additional changes made to support sorting by numbers, especially for additionalInfo fields - Bugs fixed when left join fetches were made but filters were required for specific fields --- .../model/germ/GermplasmSearchRequest.java | 27 +++++++++++++++++++ .../BrAPITestServer/model/dto/EntityType.java | 4 ++- .../model/entity/germ/GermplasmEntity.java | 19 +++++++++++++ .../service/SearchQueryBuilder.java | 22 ++++++++------- .../service/germ/GermplasmService.java | 13 +++++---- 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/main/java/io/swagger/model/germ/GermplasmSearchRequest.java b/src/main/java/io/swagger/model/germ/GermplasmSearchRequest.java index 79bbf045..d1acd966 100644 --- a/src/main/java/io/swagger/model/germ/GermplasmSearchRequest.java +++ b/src/main/java/io/swagger/model/germ/GermplasmSearchRequest.java @@ -3,12 +3,34 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.model.SearchRequest; +import org.brapi.test.BrAPITestServer.model.dto.EntityColumnNameAndType; +import org.brapi.test.BrAPITestServer.model.dto.EntityType; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; public class GermplasmSearchRequest extends SearchRequest { + + // Key - allowed sort or field filter name for this entity + // Value = entity field name and type that represents the submitted field. Used later on in query building. + private static final Map ALLOWED_SORT_AND_FILTER_FIELDS = + Map.ofEntries( + // accessionNumber = GID for DeltaBreed + Map.entry("accessionNumber", new EntityColumnNameAndType("accessionNumber", EntityType.NUMBER)), + Map.entry("defaultDisplayName", new EntityColumnNameAndType("defaultDisplayName", EntityType.TEXT)), + Map.entry("breedingMethod", new EntityColumnNameAndType("methodName", EntityType.TEXT)), + Map.entry("seedSource", new EntityColumnNameAndType("seedSource", EntityType.TEXT)), + Map.entry("pedigree",new EntityColumnNameAndType("pedigree.pedigreeString", EntityType.TEXT)), + Map.entry("femaleParentGID", new EntityColumnNameAndType("femaleParentGid", EntityType.NUMBER)), + Map.entry("maleParentGID", new EntityColumnNameAndType("maleParentGid", EntityType.NUMBER)), + Map.entry("createdDate", new EntityColumnNameAndType("createdDate", EntityType.TEXT)), + Map.entry("createdBy", new EntityColumnNameAndType("createdBy", EntityType.TEXT)), + Map.entry("importEntryNumber", new EntityColumnNameAndType("importEntryNumber", EntityType.NUMBER)), + Map.entry("synonyms", new EntityColumnNameAndType("*synonyms.synonym", EntityType.TEXT)) + ); + @JsonProperty("accessionNumbers") private List accessionNumbers = null; @@ -621,4 +643,9 @@ public Integer getTotalParameterCount() { return count; } + + @Override + public Map getEntityColAndTypeBySubmittedNameMap() { + return ALLOWED_SORT_AND_FILTER_FIELDS; + } } diff --git a/src/main/java/org/brapi/test/BrAPITestServer/model/dto/EntityType.java b/src/main/java/org/brapi/test/BrAPITestServer/model/dto/EntityType.java index e69aa988..8f1156a8 100644 --- a/src/main/java/org/brapi/test/BrAPITestServer/model/dto/EntityType.java +++ b/src/main/java/org/brapi/test/BrAPITestServer/model/dto/EntityType.java @@ -3,6 +3,8 @@ public enum EntityType { TEXT, UUID, - BOOLEAN + BOOLEAN, + // Sorting is done at the database schema/formula level for numbers. See GermplasmEntity.maleParentGid. No changes to filtering required for this datatype. + NUMBER // Add any other entity data types we should filter different in SearchQueryBuilder here } diff --git a/src/main/java/org/brapi/test/BrAPITestServer/model/entity/germ/GermplasmEntity.java b/src/main/java/org/brapi/test/BrAPITestServer/model/entity/germ/GermplasmEntity.java index 8b464b85..821c2873 100644 --- a/src/main/java/org/brapi/test/BrAPITestServer/model/entity/germ/GermplasmEntity.java +++ b/src/main/java/org/brapi/test/BrAPITestServer/model/entity/germ/GermplasmEntity.java @@ -15,6 +15,7 @@ import org.brapi.test.BrAPITestServer.model.entity.pheno.TaxonEntity; import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Formula; import org.hibernate.annotations.Where; import java.util.ArrayList; @@ -100,6 +101,24 @@ public class GermplasmEntity extends BrAPIPrimaryEntity { @ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY) private ProgramEntity program; + @Formula("(additional_info #>> '{createdDate}')") + private String createdDate; + + @Formula("(additional_info #>> '{createdBy,userName}')") + private String createdBy; + + @Formula("(additional_info #>> '{breedingMethod}')") + private String methodName; + + @Formula("cast(additional_info #>> '{femaleParentGid}' AS INTEGER)") + private Integer femaleParentGid; + + @Formula("cast(additional_info #>> '{maleParentGid}' AS INTEGER)") + private Integer maleParentGid; + + @Formula("cast(additional_info #>> '{importEntryNumber}' AS INTEGER)") + private Integer importEntryNumber; + public GermplasmInstituteEntity getHostInstitute() { if (getInstitutes() != null) { for (GermplasmInstituteEntity institute : getInstitutes()) { diff --git a/src/main/java/org/brapi/test/BrAPITestServer/service/SearchQueryBuilder.java b/src/main/java/org/brapi/test/BrAPITestServer/service/SearchQueryBuilder.java index d18a30a0..d70faf7c 100644 --- a/src/main/java/org/brapi/test/BrAPITestServer/service/SearchQueryBuilder.java +++ b/src/main/java/org/brapi/test/BrAPITestServer/service/SearchQueryBuilder.java @@ -46,12 +46,7 @@ public String getQuery() { } public String getIdQuery() { - if (sortClause.isEmpty()) { - // By default, sort on entity id to have query result remain idempotent - sortClause = defaultSort; - } - - return selectOnlyIds + whereClause + sortClause; + return selectOnlyIds + whereClause; } public Map getParams() { @@ -150,7 +145,8 @@ public SearchQueryBuilder appendLike(String like, String columnName) { return this; } - public SearchQueryBuilder appendLikeIDs(String like, String columnName) { + // Used to convert non-string fields to string and use a like filter comparison + public SearchQueryBuilder appendLikeString(String like, String columnName) { String paramName = paramFilterPattern(columnName); if (like != null) { @@ -290,6 +286,8 @@ public SearchQueryBuilder join(String join, String name) { this.selectClause += "JOIN " + entityPrefix(join) + " " + paramFilter(name) + " "; this.selectOnlyIds += "JOIN " + entityPrefix(join) + " " + paramFilter(name) + " "; this.joinedTables.add(join); + } else if (joinedFetchedTables.contains(join) && !this.joinedTables.contains(join)) { + this.selectOnlyIds += "JOIN " + entityPrefix(join) + " " + paramFilter(name) + " "; } return this; } @@ -416,6 +414,12 @@ public SearchQueryBuilder sortBy(List sortBy, Map resetSortClause() { + this.sortClause = ""; + return this; + } + private void buildSort(SortBy sort) { this.sortClause += entityPrefix(sort.getSortedOn()) + " " + sort.getSortOrder() + " "; } @@ -446,8 +450,8 @@ public SearchQueryBuilder filterBy(List filterBy, Map findGermplasm(@Valid GermplasmSearchRequest request, Meta return germplasms; } - public List findGermplasmWithoutPaging(@Valid GermplasmSearchRequest request) { + public List findGermplasmWithoutPaging(@Valid GermplasmSearchRequest request) throws BrAPIServerException { List entities = findGermplasmEntitiesWithoutPaging(request); return entities.stream().map(this::convertFromEntity).collect(Collectors.toList()); } @@ -159,7 +159,7 @@ public Page findGermplasmEntities(@Valid GermplasmSearchRequest } // TODO: Investigate/consider removing this code. It is largely unused by DeltaBreed now, as paginating is the least memory exhaustive option. [BI-3020] - public List findGermplasmEntitiesWithoutPaging(@Valid GermplasmSearchRequest request) { + public List findGermplasmEntitiesWithoutPaging(@Valid GermplasmSearchRequest request) throws BrAPIServerException { SearchQueryBuilder searchQuery = buildGermplasmSearchQuery(request); List germs = germplasmRepository.findAllBySearch(searchQuery); @@ -170,7 +170,7 @@ public List findGermplasmEntitiesWithoutPaging(@Valid Germplasm return germs; } - private SearchQueryBuilder buildGermplasmSearchQuery(GermplasmSearchRequest request) { + private SearchQueryBuilder buildGermplasmSearchQuery(GermplasmSearchRequest request) throws BrAPIServerException { SearchQueryBuilder searchQuery = new SearchQueryBuilder( GermplasmEntity.class); searchQuery.leftJoinFetch("synonyms", "synonyms") @@ -207,7 +207,9 @@ private SearchQueryBuilder buildGermplasmSearchQuery(GermplasmS // .appendList(request.getProgenyDbIds(), "*progeny.germplasmDbId") .appendList(request.getGenus(), "genus").appendList(request.getSpecies(), "species") .appendNamesList(request.getBinomialNames(), "genus", "genus", "species") - .appendList(request.getFamilyCodes(), "familyCode"); + .appendList(request.getFamilyCodes(), "familyCode") + .filterBy(request.getFilterBy(), request.getEntityColAndTypeBySubmittedNameMap()) + .sortBy(request.getSortByElements(), request.getEntityColAndTypeBySubmittedNameMap()); return searchQuery; } @@ -339,7 +341,8 @@ private void fetchRemainingGermCollectionsUsingQuery(SearchQueryBuilder