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

feat(openchallenges): extend EDAM endpoint with sorting capabilities #2644

Merged
merged 6 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/Cha
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/ChallengeSubmissionTypeDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/ChallengesPageDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/ChallengesPerYearDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/EdamConceptDirectionDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/EdamConceptDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/EdamConceptSearchQueryDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/EdamConceptSortDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/EdamConceptsPageDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/EdamDataDto.java
src/main/java/org/sagebionetworks/openchallenges/challenge/service/model/dto/EdamOperationDto.java
Expand Down
10 changes: 9 additions & 1 deletion apps/openchallenges/challenge-service/requests.http
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,12 @@ GET {{basePath}}/edamConcepts?searchTerms=sequence

### List the EDAM concepts that belong to the "data" or "format" section.

GET {{basePath}}/edamConcepts?sections=data,format
GET {{basePath}}/edamConcepts?sections=data,format

### Sort the EDAM concepts by their preferred label in asc order (asc by default)

GET {{basePath}}/edamConcepts?sort=preferred_label&direction=asc

### Sort the challenges by relevance (still by preferred label if search terms is empty)

GET {{basePath}}/edamConcepts?sort=relevance&searchTerms=data
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.sagebionetworks.openchallenges.challenge.service.model.dto.ChallengeSortDto;
import org.sagebionetworks.openchallenges.challenge.service.model.dto.ChallengeStatusDto;
import org.sagebionetworks.openchallenges.challenge.service.model.dto.ChallengeSubmissionTypeDto;
import org.sagebionetworks.openchallenges.challenge.service.model.dto.EdamConceptDirectionDto;
import org.sagebionetworks.openchallenges.challenge.service.model.dto.EdamConceptSortDto;
import org.sagebionetworks.openchallenges.challenge.service.model.dto.EdamSectionDto;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -107,6 +109,26 @@ public ChallengeSubmissionTypeDto convert(String source) {
};
}

@Bean
Converter<String, EdamConceptDirectionDto> edamConceptDirectionConverter() {
return new Converter<String, EdamConceptDirectionDto>() {
@Override
public EdamConceptDirectionDto convert(String source) {
return EdamConceptDirectionDto.fromValue(source);
}
};
}

@Bean
Converter<String, EdamConceptSortDto> edamConceptSortConverter() {
return new Converter<String, EdamConceptSortDto>() {
@Override
public EdamConceptSortDto convert(String source) {
return EdamConceptSortDto.fromValue(source);
}
};
}

@Bean
Converter<String, EdamSectionDto> edamSectionConverter() {
return new Converter<String, EdamSectionDto>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.sagebionetworks.openchallenges.challenge.service.model.dto;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.*;
import javax.annotation.Generated;
import javax.validation.constraints.*;

/** The direction to sort the results by. */
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public enum EdamConceptDirectionDto {
ASC("asc"),

DESC("desc");

private String value;

EdamConceptDirectionDto(String value) {
this.value = value;
}

@JsonValue
public String getValue() {
return value;
}

@Override
public String toString() {
return String.valueOf(value);
}

@JsonCreator
public static EdamConceptDirectionDto fromValue(String value) {
for (EdamConceptDirectionDto b : EdamConceptDirectionDto.values()) {
if (b.value.equals(value)) {
return b;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public class EdamConceptSearchQueryDto {
@JsonProperty("pageSize")
private Integer pageSize = 100;

@JsonProperty("sort")
private EdamConceptSortDto sort = EdamConceptSortDto.RELEVANCE;

@JsonProperty("direction")
private EdamConceptDirectionDto direction = null;

@JsonProperty("searchTerms")
private String searchTerms;

Expand Down Expand Up @@ -74,6 +80,46 @@ public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}

public EdamConceptSearchQueryDto sort(EdamConceptSortDto sort) {
this.sort = sort;
return this;
}

/**
* Get sort
*
* @return sort
*/
@Valid
@Schema(name = "sort", required = false)
public EdamConceptSortDto getSort() {
return sort;
}

public void setSort(EdamConceptSortDto sort) {
this.sort = sort;
}

public EdamConceptSearchQueryDto direction(EdamConceptDirectionDto direction) {
this.direction = direction;
return this;
}

/**
* Get direction
*
* @return direction
*/
@Valid
@Schema(name = "direction", required = false)
public EdamConceptDirectionDto getDirection() {
return direction;
}

public void setDirection(EdamConceptDirectionDto direction) {
this.direction = direction;
}

public EdamConceptSearchQueryDto searchTerms(String searchTerms) {
this.searchTerms = searchTerms;
return this;
Expand Down Expand Up @@ -139,13 +185,15 @@ public boolean equals(Object o) {
EdamConceptSearchQueryDto edamConceptSearchQuery = (EdamConceptSearchQueryDto) o;
return Objects.equals(this.pageNumber, edamConceptSearchQuery.pageNumber)
&& Objects.equals(this.pageSize, edamConceptSearchQuery.pageSize)
&& Objects.equals(this.sort, edamConceptSearchQuery.sort)
&& Objects.equals(this.direction, edamConceptSearchQuery.direction)
&& Objects.equals(this.searchTerms, edamConceptSearchQuery.searchTerms)
&& Objects.equals(this.sections, edamConceptSearchQuery.sections);
}

@Override
public int hashCode() {
return Objects.hash(pageNumber, pageSize, searchTerms, sections);
return Objects.hash(pageNumber, pageSize, sort, direction, searchTerms, sections);
}

@Override
Expand All @@ -154,6 +202,8 @@ public String toString() {
sb.append("class EdamConceptSearchQueryDto {\n");
sb.append(" pageNumber: ").append(toIndentedString(pageNumber)).append("\n");
sb.append(" pageSize: ").append(toIndentedString(pageSize)).append("\n");
sb.append(" sort: ").append(toIndentedString(sort)).append("\n");
sb.append(" direction: ").append(toIndentedString(direction)).append("\n");
sb.append(" searchTerms: ").append(toIndentedString(searchTerms)).append("\n");
sb.append(" sections: ").append(toIndentedString(sections)).append("\n");
sb.append("}");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.sagebionetworks.openchallenges.challenge.service.model.dto;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import java.util.*;
import javax.annotation.Generated;
import javax.validation.constraints.*;

/** What to sort results by. */
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public enum EdamConceptSortDto {
PREFERRED_LABEL("preferred_label"),

RELEVANCE("relevance");

private String value;

EdamConceptSortDto(String value) {
this.value = value;
}

@JsonValue
public String getValue() {
return value;
}

@Override
public String toString() {
return String.valueOf(value);
}

@JsonCreator
public static EdamConceptSortDto fromValue(String value) {
for (EdamConceptSortDto b : EdamConceptSortDto.values()) {
if (b.value.equals(value)) {
return b;
}
}
throw new IllegalArgumentException("Unexpected value '" + value + "'");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.search.engine.backend.types.Sortable;
import org.hibernate.search.mapper.pojo.bridge.mapping.annotation.ValueBridgeRef;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.KeywordField;
import org.sagebionetworks.openchallenges.challenge.service.model.search.EdamSectionValueBridge;
Expand Down Expand Up @@ -39,5 +41,6 @@ public class EdamConceptEntity {

@Column(name = "preferred_label", nullable = false)
@FullTextField(name = "preferred_label")
@GenericField(name = "preferred_label_sort", sortable = Sortable.YES)
private String preferredLabel;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@
import org.hibernate.search.engine.search.predicate.SearchPredicate;
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory;
import org.hibernate.search.engine.search.query.SearchResult;
import org.hibernate.search.engine.search.sort.SearchSort;
import org.hibernate.search.engine.search.sort.dsl.SearchSortFactory;
import org.hibernate.search.engine.search.sort.dsl.SortOrder;
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.sagebionetworks.openchallenges.challenge.service.exception.BadRequestException;
import org.sagebionetworks.openchallenges.challenge.service.model.dto.EdamConceptDirectionDto;
import org.sagebionetworks.openchallenges.challenge.service.model.dto.EdamConceptSearchQueryDto;
import org.sagebionetworks.openchallenges.challenge.service.model.dto.EdamSectionDto;
import org.sagebionetworks.openchallenges.challenge.service.model.entity.EdamConceptEntity;
Expand All @@ -34,6 +39,7 @@ private SearchResult<EdamConceptEntity> getSearchResult(
Pageable pageable, EdamConceptSearchQueryDto query, String[] fields) {
SearchSession searchSession = Search.session(entityManager);
SearchPredicateFactory pf = searchSession.scope(EdamConceptEntity.class).predicate();
SearchSortFactory sf = searchSession.scope(EdamConceptEntity.class).sort();
List<SearchPredicate> predicates = new ArrayList<>();

if (query.getSearchTerms() != null && !query.getSearchTerms().isBlank()) {
Expand All @@ -43,11 +49,14 @@ private SearchResult<EdamConceptEntity> getSearchResult(
predicates.add(getEdamSectionsPredicate(pf, query));
}

SearchSort sort = getSearchSort(sf, query);

SearchPredicate topLevelPredicate = buildTopLevelPredicate(pf, predicates);

return searchSession
.search(EdamConceptEntity.class)
.where(topLevelPredicate)
.sort(sort)
.fetch((int) pageable.getOffset(), pageable.getPageSize());
}

Expand Down Expand Up @@ -104,4 +113,30 @@ private SearchPredicate buildTopLevelPredicate(
})
.toPredicate();
}

private SearchSort getSearchSort(SearchSortFactory sf, EdamConceptSearchQueryDto query) {
SortOrder orderWithDefaultAsc =
query.getDirection() == EdamConceptDirectionDto.DESC ? SortOrder.DESC : SortOrder.ASC;
SortOrder orderWithDefaultDesc =
query.getDirection() == EdamConceptDirectionDto.ASC ? SortOrder.ASC : SortOrder.DESC;

SearchSort preferredLabelSort =
sf.field("preferred_label_sort").order(orderWithDefaultAsc).toSort();
SearchSort scoreSort = sf.score().order(orderWithDefaultDesc).toSort();
SearchSort relevanceSort =
(query.getSearchTerms() == null || query.getSearchTerms().isBlank())
? preferredLabelSort
: scoreSort;

switch (query.getSort()) {
case PREFERRED_LABEL -> {
return preferredLabelSort;
}
case RELEVANCE -> {
return relevanceSort;
}
default -> throw new BadRequestException(
String.format("Unhandled sorting strategy '%s'", query.getSort()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,20 @@ components:
type: object
x-java-class-annotations:
- '@lombok.Builder'
EdamConceptSort:
default: relevance
description: What to sort results by.
enum:
- preferred_label
- relevance
type: string
EdamConceptDirection:
description: The direction to sort the results by.
enum:
- asc
- desc
nullable: true
type: string
EdamSection:
description: The EDAM section (sub-ontology).
enum:
Expand All @@ -1049,6 +1063,10 @@ components:
format: int32
minimum: 1
type: integer
sort:
$ref: '#/components/schemas/EdamConceptSort'
direction:
$ref: '#/components/schemas/EdamConceptDirection'
searchTerms:
description: A string of search terms used to filter the results.
example: sequence image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ model/challengesPage.ts
model/challengesPageAllOf.ts
model/challengesPerYear.ts
model/edamConcept.ts
model/edamConceptDirection.ts
model/edamConceptSearchQuery.ts
model/edamConceptSort.ts
model/edamConceptsPage.ts
model/edamConceptsPageAllOf.ts
model/edamData.ts
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* OpenChallenges REST API
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/


/**
* The direction to sort the results by.
*/
export type EdamConceptDirection = 'asc' | 'desc';

export const EdamConceptDirection = {
Asc: 'asc' as EdamConceptDirection,
Desc: 'desc' as EdamConceptDirection
};

Loading
Loading