From bd2523521522a125ffbb2a9aa31baea74eea7230 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Wed, 27 Dec 2023 14:03:31 +0100 Subject: [PATCH 01/15] enabled all rest controllers --- .../api/v1/V1IndividualController.java | 18 +++++++++--------- .../api/v1/V1OntologyController.java | 2 +- .../api/v1/V1OntologyIndividualController.java | 2 +- .../api/v1/V1OntologyPropertyController.java | 2 +- .../api/v1/V1PropertyController.java | 8 ++++---- .../controller/api/v1/V1SearchController.java | 2 +- .../controller/api/v1/V1SelectController.java | 2 +- .../controller/api/v1/V1SuggestController.java | 2 +- .../controller/api/v2/V2ClassController.java | 2 +- .../controller/api/v2/V2EntityController.java | 2 +- .../api/v2/V2IndividualController.java | 2 +- .../api/v2/V2OntologyController.java | 2 +- .../api/v2/V2PropertyController.java | 2 +- .../api/v2/V2StatisticsController.java | 2 +- 14 files changed, 25 insertions(+), 25 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1IndividualController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1IndividualController.java index c8b900379..7c967b65b 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1IndividualController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1IndividualController.java @@ -28,7 +28,7 @@ * @date 18/08/2015 * Samples, Phenotypes and Ontologies Team, EMBL-EBI */ -@Controller +@RestController @RequestMapping("/api/individuals") @ExposesResourceFor(V1Individual.class) public class V1IndividualController implements @@ -81,7 +81,7 @@ HttpEntity> getAllIndividuals( return new ResponseEntity<>(assembler.toModel(terms, individualAssembler), HttpStatus.OK); } - + @RequestMapping(path = "/findByIdAndIsDefiningOntology/{id}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) HttpEntity> getAllIndividualsByIdAndIsDefiningOntology( @PathVariable("id") String termId, @@ -92,11 +92,11 @@ HttpEntity> getAllIndividualsByIdAndIsDefiningOntology( decoded = UriUtils.decode(termId, "UTF-8"); return getAllIndividualsByIdAndIsDefiningOntology(decoded, null, null, lang, pageable, assembler); - } - - - @RequestMapping(path = "/findByIdAndIsDefiningOntology", - produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, + } + + + @RequestMapping(path = "/findByIdAndIsDefiningOntology", + produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) HttpEntity> getAllIndividualsByIdAndIsDefiningOntology( @RequestParam(value = "iri", required = false) String iri, @@ -120,11 +120,11 @@ HttpEntity> getAllIndividualsByIdAndIsDefiningOntology( return new ResponseEntity<>(assembler.toModel(terms, individualAssembler), HttpStatus.OK); } - + @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "EntityModel not found") @ExceptionHandler(ResourceNotFoundException.class) public void handleError(HttpServletRequest req, Exception exception) { } -} \ No newline at end of file +} diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyController.java index a7bb975fc..89b708a69 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyController.java @@ -31,7 +31,7 @@ * @date 19/08/2015 * Samples, Phenotypes and Ontologies Team, EMBL-EBI */ -@Controller +@RestController @RequestMapping("/api/ontologies") @ExposesResourceFor(V1Ontology.class) public class V1OntologyController implements diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyIndividualController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyIndividualController.java index 5ea636170..75b5ca482 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyIndividualController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyIndividualController.java @@ -34,7 +34,7 @@ * @date 02/11/15 * Samples, Phenotypes and Ontologies Team, EMBL-EBI */ -@Controller +@RestController @RequestMapping("/api/ontologies") public class V1OntologyIndividualController { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyPropertyController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyPropertyController.java index 9e7441d7e..e01786710 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyPropertyController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologyPropertyController.java @@ -27,7 +27,7 @@ import javax.servlet.http.HttpServletRequest; import java.util.Arrays; -@Controller +@RestController @RequestMapping("/api/ontologies") public class V1OntologyPropertyController { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1PropertyController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1PropertyController.java index b6dce598a..3bc5a7fdc 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1PropertyController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1PropertyController.java @@ -23,7 +23,7 @@ import javax.servlet.http.HttpServletRequest; -@Controller +@RestController @RequestMapping("/api/properties") @ExposesResourceFor(V1Property.class) public class V1PropertyController implements @@ -93,8 +93,8 @@ HttpEntity> getPropertiesByIriAndIsDefiningOntology(@Path String decoded = null; decoded = UriUtils.decode(termId, "UTF-8"); return getPropertiesByIdAndIsDefiningOntology(decoded, null, null, lang, pageable, assembler); - } - + } + @RequestMapping(path = "/findByIdAndIsDefiningOntology", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) HttpEntity> getPropertiesByIdAndIsDefiningOntology( @RequestParam(value = "iri", required = false) String iri, @@ -121,7 +121,7 @@ else if (oboId != null) { return new ResponseEntity<>( assembler.toModel(terms, termAssembler), HttpStatus.OK); } - + @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "EntityModel not found") @ExceptionHandler(ResourceNotFoundException.class) public void handleError(HttpServletRequest req, Exception exception) { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java index a1f4ac641..bef135771 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java @@ -35,7 +35,7 @@ * @date 02/07/2015 * Samples, Phenotypes and Ontologies Team, EMBL-EBI */ -@Controller +@RestController public class V1SearchController { Gson gson = new Gson(); diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SelectController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SelectController.java index eeb0008ed..a1e344d8c 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SelectController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SelectController.java @@ -28,7 +28,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -@Controller +@RestController public class V1SelectController { Gson gson = new Gson(); diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SuggestController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SuggestController.java index 2cc170795..018ed1bc2 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SuggestController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SuggestController.java @@ -22,7 +22,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; -@Controller +@RestController public class V1SuggestController { Gson gson = new Gson(); diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java index d8b8b4fde..ec0e4fd6e 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java @@ -35,7 +35,7 @@ import java.util.List; import java.util.Map; -@Controller +@RestController @RequestMapping("/api/v2") public class V2ClassController { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2EntityController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2EntityController.java index 6c760ae8e..11ad5e444 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2EntityController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2EntityController.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.Map; -@Controller +@RestController @RequestMapping("/api/v2") public class V2EntityController { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2IndividualController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2IndividualController.java index bff23a360..e8342f2cd 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2IndividualController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2IndividualController.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.Map; -@Controller +@RestController @RequestMapping("/api/v2") public class V2IndividualController { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologyController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologyController.java index 6a9c8501e..ce996db00 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologyController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologyController.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.Map; -@Controller +@RestController @RequestMapping("/api/v2/ontologies") public class V2OntologyController { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2PropertyController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2PropertyController.java index ee847f3cd..9d690a53c 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2PropertyController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2PropertyController.java @@ -25,7 +25,7 @@ import java.util.List; import java.util.Map; -@Controller +@RestController @RequestMapping("/api/v2") public class V2PropertyController { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2StatisticsController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2StatisticsController.java index 0af7b2460..31b51eea0 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2StatisticsController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2StatisticsController.java @@ -20,7 +20,7 @@ import java.util.HashMap; import java.util.Map; -@Controller +@RestController @RequestMapping("/api/v2/stats") public class V2StatisticsController { From 8811ab9ae4b9ee5e1091611ae1b4ede2df08c229 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Wed, 27 Dec 2023 14:30:21 +0100 Subject: [PATCH 02/15] added imports --- .../ac/ebi/spot/ols/controller/api/v1/V1ApiUnavailable.java | 5 ++--- .../ebi/spot/ols/controller/api/v1/V1SearchController.java | 4 +--- .../ebi/spot/ols/controller/api/v1/V1SelectController.java | 4 +--- .../ebi/spot/ols/controller/api/v1/V1SuggestController.java | 4 +--- .../ac/ebi/spot/ols/controller/api/v2/V2ClassController.java | 5 +---- .../ebi/spot/ols/controller/api/v2/V2EntityController.java | 5 +---- .../spot/ols/controller/api/v2/V2IndividualController.java | 5 +---- .../ebi/spot/ols/controller/api/v2/V2OntologyController.java | 5 +---- .../spot/ols/controller/api/v2/V2StatisticsController.java | 3 +-- 9 files changed, 10 insertions(+), 30 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1ApiUnavailable.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1ApiUnavailable.java index 4452d07bf..dabe0309e 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1ApiUnavailable.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1ApiUnavailable.java @@ -6,8 +6,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; @@ -16,7 +15,7 @@ * @date 27/09/2016 * Samples, Phenotypes and Ontologies Team, EMBL-EBI */ -@Controller +@RestController public class V1ApiUnavailable { @RequestMapping(path = "/api/unavailable", produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java index bef135771..e4c0273a0 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java @@ -11,9 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import uk.ac.ebi.spot.ols.repository.Validation; import uk.ac.ebi.spot.ols.repository.solr.OlsSolrClient; import uk.ac.ebi.spot.ols.repository.transforms.LocalizationTransform; diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SelectController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SelectController.java index a1e344d8c..300640643 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SelectController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SelectController.java @@ -11,9 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import uk.ac.ebi.spot.ols.repository.Validation; import uk.ac.ebi.spot.ols.repository.solr.OlsSolrClient; import uk.ac.ebi.spot.ols.repository.transforms.LocalizationTransform; diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SuggestController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SuggestController.java index 018ed1bc2..74db8821a 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SuggestController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SuggestController.java @@ -10,9 +10,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import uk.ac.ebi.spot.ols.repository.Validation; import uk.ac.ebi.spot.ols.repository.solr.OlsSolrClient; import uk.ac.ebi.spot.ols.repository.v1.V1OntologyRepository; diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java index ec0e4fd6e..3c1fd9e9a 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java @@ -15,10 +15,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import org.springframework.web.util.UriUtils; import uk.ac.ebi.spot.ols.controller.api.v2.helpers.DynamicQueryHelper; import uk.ac.ebi.spot.ols.controller.api.v2.responses.V2PagedAndFacetedResponse; diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2EntityController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2EntityController.java index 11ad5e444..8d8c254bd 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2EntityController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2EntityController.java @@ -10,10 +10,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import org.springframework.web.util.UriUtils; import uk.ac.ebi.spot.ols.controller.api.v2.helpers.DynamicQueryHelper; import uk.ac.ebi.spot.ols.controller.api.v2.responses.V2PagedAndFacetedResponse; diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2IndividualController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2IndividualController.java index e8342f2cd..c381840b5 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2IndividualController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2IndividualController.java @@ -11,10 +11,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import org.springframework.web.util.UriUtils; import uk.ac.ebi.spot.ols.controller.api.v2.helpers.DynamicQueryHelper; import uk.ac.ebi.spot.ols.controller.api.v2.responses.V2PagedAndFacetedResponse; diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologyController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologyController.java index ce996db00..1d322cd1d 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologyController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologyController.java @@ -12,10 +12,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import uk.ac.ebi.spot.ols.controller.api.v2.helpers.DynamicQueryHelper; import uk.ac.ebi.spot.ols.controller.api.v2.responses.V2PagedAndFacetedResponse; import uk.ac.ebi.spot.ols.model.v2.V2Entity; diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2StatisticsController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2StatisticsController.java index 31b51eea0..f676af1dc 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2StatisticsController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2StatisticsController.java @@ -11,8 +11,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; import uk.ac.ebi.spot.ols.model.v2.V2Statistics; import uk.ac.ebi.spot.ols.repository.solr.OlsSolrClient; From 865418e08b53be6ae63c13903130da95e5da5940 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Wed, 3 Jan 2024 14:49:54 +0100 Subject: [PATCH 03/15] added skos tree generation controllers --- .../ols/controller/api/v1/TopConceptEnum.java | 7 + .../v1/V1OntologySKOSConceptController.java | 296 ++++++++++++++++++ .../ebi/spot/ols/repository/v1/TreeNode.java | 108 +++++++ .../ols/repository/v1/V1TermRepository.java | 233 ++++++++++++++ dataload/configs/skos_ontologies.json | 113 +++++++ 5 files changed, 757 insertions(+) create mode 100644 backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/TopConceptEnum.java create mode 100644 backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java create mode 100644 backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/TreeNode.java create mode 100644 dataload/configs/skos_ontologies.json diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/TopConceptEnum.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/TopConceptEnum.java new file mode 100644 index 000000000..95aceccbc --- /dev/null +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/TopConceptEnum.java @@ -0,0 +1,7 @@ +package uk.ac.ebi.spot.ols.controller.api.v1; + +public enum TopConceptEnum { + SCHEMA, + TOPCONCEPTOF_PROPERTY, + RELATIONSHIPS, +} diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java new file mode 100644 index 000000000..6f2dd346a --- /dev/null +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java @@ -0,0 +1,296 @@ +package uk.ac.ebi.spot.ols.controller.api.v1; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.data.web.PagedResourcesAssembler; +import org.springframework.hateoas.MediaTypes; +import org.springframework.hateoas.PagedModel; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriUtils; + +import uk.ac.ebi.spot.ols.model.v1.V1Term; +import uk.ac.ebi.spot.ols.repository.v1.TreeNode; +import uk.ac.ebi.spot.ols.repository.v1.V1TermRepository; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Simon Jupp + * @date 02/11/15 + * Samples, Phenotypes and Ontologies Team, EMBL-EBI + */ +@RestController +@RequestMapping("/api/ontologies") +@Tag(name = "v1-ontology-skos-controller", description = "SKOS concept hierarchies and relations extracted from individuals (instances) from a particular ontology in this service") +public class V1OntologySKOSConceptController { + + private Logger log = LoggerFactory.getLogger(getClass()); + + @Autowired + private V1TermRepository termRepository; + + @Autowired + V1TermAssembler termAssembler; + + @Operation(description = "Get complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/conceptrelations/{iri} method with broader or narrower concept relations.") + @RequestMapping(path = "/{onto}/concepthierarchy", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + HttpEntity>> getSKOSConceptHierarchyByOntology( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "infer top concepts by schema (hasTopConcept) or TopConceptOf property or broader/narrower relationships", required = true) + @RequestParam(value = "find_roots", required = true, defaultValue = "SCHEMA") TopConceptEnum topConceptIdentification, + @Parameter(description = "infer from narrower or broader relationships", required = true) + @RequestParam(value = "narrower", required = true, defaultValue = "false") boolean narrower, + @Parameter(description = "Extract the whole tree with children or only the top concepts", required = true) + @RequestParam(value = "with_children", required = true, defaultValue = "false") boolean withChildren, + @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) { + ontologyId = ontologyId.toLowerCase(); + if (TopConceptEnum.RELATIONSHIPS == topConceptIdentification) + return new ResponseEntity<>(termRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable), HttpStatus.OK); + else + return new ResponseEntity<>(termRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable), HttpStatus.OK); + } + + @Operation(description = "Display complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/displayconceptrelations/{iri} method with broader or narrower concept relations.") + @RequestMapping(path = "/{onto}/displayconcepthierarchy", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @ResponseBody + HttpEntity displaySKOSConceptHierarchyByOntology( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "infer top concepts by schema (hasTopConcept) or TopConceptOf property or broader/narrower relationships", required = true) + @RequestParam(value = "find_roots", required = true, defaultValue = "SCHEMA") TopConceptEnum topConceptIdentification, + @Parameter(description = "infer from narrower or broader relationships", required = true) + @RequestParam(value = "narrower", required = true, defaultValue = "false") boolean narrower, + @Parameter(description = "Extract the whole tree with children or only the top concepts", required = true) + @RequestParam(value = "with_children", required = true, defaultValue = "false") boolean withChildren, + @Parameter(description = "display related concepts", required = true) + @RequestParam(value = "display_related", required = true, defaultValue = "false") boolean displayRelated, + @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) { + ontologyId = ontologyId.toLowerCase(); + List> rootIndividuals = null; + if(TopConceptEnum.RELATIONSHIPS == topConceptIdentification) + rootIndividuals = termRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable); + else + rootIndividuals = termRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable); + StringBuilder sb = new StringBuilder(); + for (TreeNode root : rootIndividuals) { + sb.append(root.getIndex() + " , "+ root.getData().label + " , " + root.getData().iri).append("\n"); + sb.append(generateConceptHierarchyTextByOntology(root, displayRelated)); + } + + return new HttpEntity(sb.toString()); + } + + @Operation(description = "Get partial SKOS concept hierarchy based on the encoded iri of the designated top concept") + @RequestMapping(path = "/{onto}/concepthierarchy/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + HttpEntity> getSKOSConceptHierarchyByOntologyAndIri( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "infer from narrower or broader relationships", required = true) + @RequestParam(value = "narrower", required = true, defaultValue = "false") boolean narrower, + @Parameter(description = "index value for the root term", required = true) + @RequestParam(value = "index", required = true, defaultValue = "1") String index, + @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) { + ontologyId = ontologyId.toLowerCase(); + TreeNode topConcept = new TreeNode(new V1Term()); + String decodedIri; + decodedIri = UriUtils.decode(iri, "UTF-8"); + topConcept = termRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); + + if (topConcept.getData().iri == null) + throw new ResourceNotFoundException("No roots could be found for " + ontologyId ); + return new ResponseEntity<>(topConcept, HttpStatus.OK); + } + + @Operation(description = "Display partial SKOS concept hierarchy based on the encoded iri of the designated top concept") + @RequestMapping(path = "/{onto}/displayconcepthierarchy/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @ResponseBody + HttpEntity displaySKOSConceptHierarchyByOntologyAndIri( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "infer from narrower or broader relationships", required = true) + @RequestParam(value = "narrower", required = true, defaultValue = "false") boolean narrower, + @Parameter(description = "display related concepts", required = true) + @RequestParam(value = "display_related", required = true, defaultValue = "false") boolean displayRelated, + @Parameter(description = "index value for the root term", required = true) + @RequestParam(value = "index", required = true, defaultValue = "1") String index, + @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) { + ontologyId = ontologyId.toLowerCase(); + TreeNode topConcept = new TreeNode(new V1Term()); + String decodedIri; + StringBuilder sb = new StringBuilder(); + decodedIri = UriUtils.decode(iri, "UTF-8"); + topConcept = termRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); + + sb.append(topConcept.getIndex() + " , "+ topConcept.getData().label + " , " + topConcept.getData().iri).append("\n"); + sb.append(generateConceptHierarchyTextByOntology(topConcept, displayRelated)); + + return new HttpEntity(sb.toString()); + } + + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format.") + @RequestMapping(path = "/{onto}/conceptrelations/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + public HttpEntity> findRelatedConcepts( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "skos based concept relation type", required = true) + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable, + PagedResourcesAssembler assembler) { + + ontologyId = ontologyId.toLowerCase(); + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + related = termRepository.findRelated(ontologyId, decodedIri, relationType,lang); + + + final int start = (int)pageable.getOffset(); + final int end = Math.min((start + pageable.getPageSize()), related.size()); + Page conceptPage = new PageImpl<>(related.subList(start, end), pageable, related.size()); + + return new ResponseEntity<>( assembler.toModel(conceptPage), HttpStatus.OK); + + } + @Operation(description = "Broader, Narrower and Related concept relations of a concept are displayed as text if the concept iri is provided in encoded format.") + @RequestMapping(path = "/{onto}/displayconceptrelations/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @ResponseBody + public HttpEntity displayRelatedConcepts( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "skos based concept relation type", required = true) + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable, + PagedResourcesAssembler assembler) { + StringBuilder sb = new StringBuilder(); + ontologyId = ontologyId.toLowerCase(); + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + related = termRepository.findRelated(ontologyId, decodedIri, relationType,lang); + + final int start = (int)pageable.getOffset(); + final int end = Math.min((start + pageable.getPageSize()), related.size()); + Page conceptPage = new PageImpl<>(related.subList(start, end), pageable, related.size()); + int count = 0; + for (V1Term individual : conceptPage.getContent()) + sb.append(++count).append(" , ").append(individual.label).append(" , ").append(individual.iri).append("\n"); + + return new HttpEntity<>( sb.toString()); + + } + + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format. The relationship is identified indirectly based on the related concept's relation to the concept in question. This requires traversing all the available concepts and checking if they are related to the concept in question. For this reason, this method is relatively slower than the displayconceptrelations method. Nevertheless, it enables to identify unforeseen relations of the concept in question") + @RequestMapping(path = "/{onto}/conceptrelationsindirectly/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + public HttpEntity> findRelatedConceptsIndirectly( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "skos based concept relation type", required = true) + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) { + + ontologyId = ontologyId.toLowerCase(); + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + related = termRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType, obsoletes,lang,pageable); + + return new ResponseEntity<>( related, HttpStatus.OK); + + } + + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format. The relationship is identified indirectly based on the related concept's relation to the concept in question. This requires traversing all the available concepts and checking if they are related to the concept in question. For this reason, this method is relatively slower than the displayconceptrelations method. Nevertheless, it enables to identify unforeseen relations of the concept in question") + @RequestMapping(path = "/{onto}/displayconceptrelationsindirectly/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @ResponseBody + public HttpEntity displayRelatedConceptsIndirectly( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "skos based concept relation type", required = true) + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @Parameter(description = "Page size to retrieve individuals", required = true) + @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) { + StringBuilder sb = new StringBuilder(); + ontologyId = ontologyId.toLowerCase(); + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + related = termRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType, obsoletes,lang,pageable); + + int count = 0; + for (V1Term individual : related) + sb.append(++count).append(" , ").append(individual.label).append(" , ").append(individual.iri).append("\n"); + + + return new ResponseEntity<>( sb.toString(), HttpStatus.OK); + + } + + public StringBuilder generateConceptHierarchyTextByOntology(TreeNode rootConcept, boolean displayRelated) { + StringBuilder sb = new StringBuilder(); + for (TreeNode childConcept : rootConcept.getChildren()) { + sb.append(childConcept.getIndex() + " , "+ childConcept.getData().label + " , " + childConcept.getData().iri).append("\n"); + sb.append(generateConceptHierarchyTextByOntology(childConcept,displayRelated)); + } + if(displayRelated) + for (TreeNode relatedConcept : rootConcept.getRelated()) { + sb.append(relatedConcept.getIndex() + " , "+ relatedConcept.getData().label + " , " + relatedConcept.getData().iri).append("\n"); + sb.append(generateConceptHierarchyTextByOntology(relatedConcept,displayRelated)); + } + return sb; + } + + @RequestMapping(method = RequestMethod.GET, produces = {MediaType.TEXT_PLAIN_VALUE}, value = "/removeConceptTreeCache") + public HttpEntity removeConceptTreeCache() { + return new HttpEntity(termRepository.removeConceptTreeCache()); + } + + @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found") + @ExceptionHandler(ResourceNotFoundException.class) + public void handleError(HttpServletRequest req, Exception exception) { + } + +} diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/TreeNode.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/TreeNode.java new file mode 100644 index 000000000..1ca07e9b3 --- /dev/null +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/TreeNode.java @@ -0,0 +1,108 @@ +package uk.ac.ebi.spot.ols.repository.v1; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; + +public class TreeNode implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -343190255910189166L; + private Collection> children = new ArrayList>(); + private Collection> related = new ArrayList>(); + private Collection> parent = new ArrayList>(); + private String index; + private T data = null; + + public TreeNode(T data) { + this.data = data; + } + + public TreeNode(T data, Collection> parent) { + this.data = data; + this.parent = parent; + } + + public Collection> getChildren() { + return children; + } + public void setChildren(Collection> children) { + this.children = children; + } + + public void addChild(T data) { + TreeNode child = new TreeNode(data); + this.children.add(child); + } + + public void addChild(TreeNode child) { + this.children.add(child); + } + + public void addRelated(T data) { + TreeNode related = new TreeNode(data); + this.related.add(related); + } + + public void addRelated(TreeNode related) { + this.related.add(related); + } + + public void addParent(T data) { + TreeNode parent = new TreeNode(data); + this.parent.add(parent); + } + + public void addParent(TreeNode parent) { + this.parent.add(parent); + } + + public Collection> getRelated() { + return related; + } + public void setRelated(Collection> related) { + this.related = related; + } + public Collection> getParent() { + return parent; + } + public void setParent(Collection> parent) { + this.parent = parent; + } + public String getIndex() { + return index; + } + public void setIndex(String index) { + this.index = index; + } + + public T getData() { + return this.data; + } + + public void setData(T data) { + this.data = data; + } + + public boolean isRoot() { + return this.parent.size() == 0; + } + + public boolean isLeaf() { + return this.children.size() == 0; + } + + public void resetParent() { + this.parent = new ArrayList>(); + } + + public void resetChildren() { + this.children = new ArrayList>(); + } + + public void resetRelated() { + this.related = new ArrayList>(); + } +} diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java index f4965be37..ec078c81d 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java @@ -4,7 +4,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; import uk.ac.ebi.spot.ols.model.v1.V1Individual; @@ -16,9 +19,13 @@ import uk.ac.ebi.spot.ols.repository.solr.SearchType; import uk.ac.ebi.spot.ols.repository.v1.mappers.V1TermMapper; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; @Component public class V1TermRepository { @@ -290,5 +297,231 @@ public Page findAllByOboIdAndIsDefiningOntology(String oboId, String lan public Page getInstances(String ontologyId, String iri, Pageable pageable) { throw new RuntimeException(); } + + + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#schema).concat('-').concat(#narrower).concat('-').concat(#withChildren)") + public List> conceptTree (String ontologyId, boolean schema, boolean narrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable){ + Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); + List listOfTerms = new ArrayList(); + listOfTerms.addAll(terms.getContent()); + + while(terms.hasNext()) { + terms = this.findAllByOntology(ontologyId, obsoletes, lang, terms.nextPageable()); + listOfTerms.addAll(terms.getContent()); + } + + List> rootIndividuals = new ArrayList>(); + int count = 0; + + if(schema) { + for (V1Term indiv : listOfTerms) + if (indiv.annotation.get("hasTopConcept") != null) { + for (String iriTopConcept : (LinkedHashSet) indiv.annotation.get("hasTopConcept")) { + V1Term topConceptIndividual = findIndividual(listOfTerms,iriTopConcept); + TreeNode topConcept = new TreeNode(topConceptIndividual); + topConcept.setIndex(String.valueOf(++count)); + if(withChildren) { + if(narrower) + populateChildrenandRelatedByNarrower(topConceptIndividual,topConcept,listOfTerms); + else + populateChildrenandRelatedByBroader(topConceptIndividual,topConcept,listOfTerms); + } + rootIndividuals.add(topConcept); + } + } + } else for (V1Term individual : listOfTerms) { + TreeNode tree = new TreeNode(individual); + + if (tree.isRoot() && individual.annotation.get("topConceptOf") != null) { + tree.setIndex(String.valueOf(++count)); + if(withChildren) { + if(narrower) + populateChildrenandRelatedByNarrower(individual,tree,listOfTerms); + else + populateChildrenandRelatedByBroader(individual,tree,listOfTerms); + } + rootIndividuals.add(tree); + } + } + + return rootIndividuals; + } + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#narrower).concat('-').concat(#withChildren)") + public List> conceptTreeWithoutTop (String ontologyId, boolean narrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable){ + Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); + List listOfTerms = new ArrayList(); + listOfTerms.addAll(terms.getContent()); + + while(terms.hasNext()) { + terms = this.findAllByOntology(ontologyId, obsoletes, lang, terms.nextPageable()); + listOfTerms.addAll(terms.getContent()); + } + + Set rootIRIs = new HashSet(); + List> rootIndividuals = new ArrayList>(); + int count = 0; + if(!narrower) { + for (V1Term individual : listOfTerms) { + if (individual.annotation.get("broader") != null) { + for (String iriBroader : (LinkedHashSet) individual.annotation.get("broader")) { + V1Term broaderIndividual = findIndividual(listOfTerms,iriBroader); + if (broaderIndividual.annotation.get("broader") == null) { + rootIRIs.add(iriBroader); + } + } + } + } + + for (String iri : rootIRIs) { + V1Term topConceptIndividual = findIndividual(listOfTerms, iri); + TreeNode topConcept = new TreeNode(topConceptIndividual); + topConcept.setIndex(String.valueOf(++count)); + if(withChildren) + populateChildrenandRelatedByBroader(topConceptIndividual,topConcept,listOfTerms); + rootIndividuals.add(topConcept); + } + + } else { + for (V1Term individual : listOfTerms) { + if (individual.annotation.get("narrower") != null) { + boolean root = true; + for (V1Term indiv : listOfTerms) { + if (indiv.annotation.get("narrower") != null) { + for (String iriNarrower : (LinkedHashSet) indiv.annotation.get("narrower")) { + if (individual.iri.equals(iriNarrower)) + root = false; + } + } + } + + if(root) { + TreeNode topConcept = new TreeNode(individual); + topConcept.setIndex(String.valueOf(++count)); + if(withChildren) + populateChildrenandRelatedByNarrower(individual,topConcept,listOfTerms); + rootIndividuals.add(topConcept); + } + } + } + } + + return rootIndividuals; + } + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat('s').concat('-').concat(#iri).concat('-').concat(#narrower).concat('-').concat(#index)") + public TreeNode conceptSubTree(String ontologyId, String iri, boolean narrower, String index, Boolean obsoletes, String lang, Pageable pageable){ + Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); + List listOfTerms = new ArrayList(); + listOfTerms.addAll(terms.getContent()); + + while(terms.hasNext()) { + terms = this.findAllByOntology(ontologyId, obsoletes, lang, terms.nextPageable()); + listOfTerms.addAll(terms.getContent()); + } + + V1Term topConceptIndividual = findIndividual(listOfTerms,iri); + TreeNode topConcept = new TreeNode(topConceptIndividual); + topConcept.setIndex(index); + if(narrower) + populateChildrenandRelatedByNarrower(topConceptIndividual,topConcept,listOfTerms); + else + populateChildrenandRelatedByBroader(topConceptIndividual,topConcept,listOfTerms); + + return topConcept; + } + + public V1Term findIndividual(List wholeList, String iri) { + for (V1Term individual : wholeList) + if(individual.iri.equals(iri)) + return individual; + return new V1Term(); + } + + public List findRelated(String ontologyId, String iri, String relationType, String lang) { + List related = new ArrayList(); + V1Term individual = this.findByOntologyAndIri(ontologyId, iri, lang); + if (individual != null) + if (individual.annotation.get(relationType) != null) + for (String iriBroader : (LinkedHashSet) individual.annotation.get(relationType)) + related.add(this.findByOntologyAndIri(ontologyId, iriBroader, lang)); + + return related; + } + + public ListfindRelatedIndirectly(String ontologyId, String iri, String relationType, Boolean obsoletes, String lang, Pageable pageable){ + List related = new ArrayList(); + + V1Term individual = this.findByOntologyAndIri(ontologyId, iri, lang); + if(individual == null) + return related; + if(individual.iri == null) + return related; + + Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); + List listOfTerms = new ArrayList(); + listOfTerms.addAll(terms.getContent()); + + while(terms.hasNext()) { + terms = this.findAllByOntology(ontologyId, obsoletes, lang, terms.nextPageable()); + listOfTerms.addAll(terms.getContent()); + } + + for (V1Term term : listOfTerms) { + if (term != null) + if (term.annotation.get(relationType) != null) + for (String iriRelated : (LinkedHashSet) term.annotation.get(relationType)) + if(iriRelated.equals(iri)) + related.add(term); + } + + return related; + } + + public void populateChildrenandRelatedByNarrower(V1Term individual, TreeNode tree, List listOfTerms ) { + + if (individual.annotation.get("related") != null) + for (String iriRelated : (LinkedHashSet) individual.annotation.get("related")) { + TreeNode related = new TreeNode(findIndividual(listOfTerms,iriRelated)); + related.setIndex(tree.getIndex()+ ".related"); + tree.addRelated(related); + } + int count = 0; + if (individual.annotation.get("narrower") != null) + for (String iriChild : (LinkedHashSet) individual.annotation.get("narrower")) { + V1Term childIndividual = findIndividual(listOfTerms,iriChild); + TreeNode child = new TreeNode(childIndividual); + child.setIndex(tree.getIndex()+"."+ ++count); + populateChildrenandRelatedByNarrower(childIndividual,child,listOfTerms); + tree.addChild(child); + } + } + + public void populateChildrenandRelatedByBroader(V1Term individual, TreeNode tree, List listOfTerms) { + if (individual.annotation.get("related") != null) + for (String iriRelated : (LinkedHashSet) individual.annotation.get("related")) { + TreeNode related = new TreeNode(findIndividual(listOfTerms,iriRelated)); + related.setIndex(tree.getIndex()+ ".related"); + tree.addRelated(related); + } + int count = 0; + for ( V1Term indiv : listOfTerms) { + if (indiv.annotation.get("broader") != null) + for (String iriBroader : (LinkedHashSet) indiv.annotation.get("broader")) + if(individual.iri != null) + if (individual.iri.equals(iriBroader)) { + TreeNode child = new TreeNode(indiv); + child.setIndex(tree.getIndex()+"."+ ++count); + populateChildrenandRelatedByBroader(indiv,child,listOfTerms); + tree.addChild(child); + } + } + } + + @CacheEvict(value="concepttree", allEntries=true) + public String removeConceptTreeCache() { + return "All concept tree cache removed!"; + } } diff --git a/dataload/configs/skos_ontologies.json b/dataload/configs/skos_ontologies.json new file mode 100644 index 000000000..403f54325 --- /dev/null +++ b/dataload/configs/skos_ontologies.json @@ -0,0 +1,113 @@ +{ + "ontologies": [ + { + "title": "PhySH - Physics Subject Headings", + "preferredPrefix": "physh", + "description": "PhySH (Physics Subject Headings) is a physics classification scheme developed by APS to organize journal, meeting, and other content by topic.", + "homepage": "https://physh.org/", + "tracker": null, + "logo": null, + "annotations": null, + "oboSlims": false, + "preferredRootTerms": [], + "allowDownload": false, + "classifications": null, + "license": null, + "repoUrl": null, + "uri": "https://raw.githubusercontent.com/physh-org/PhySH/master/physh.ttl", + "id": "physh", + "mailing_list": null, + "ontology_purl": "https://raw.githubusercontent.com/physh-org/PhySH/master/physh.ttl", + "reasoner": "NONE", + "label_property": "https://physh.org/rdf/2018/01/01/core#prefLabel", + "definition_property": [ + "http://www.w3.org/2004/02/skos/core#definition", + "http://purl.org/dc/terms/description" + ], + "synonym_property": [ + "http://www.w3.org/2004/02/skos/core#altLabel" + ], + "hierarchical_property": [ + "http://www.w3.org/2004/02/skos/core#broader", + "https://physh.org/rdf/2018/01/01/core#inDiscipline", + "https://physh.org/rdf/2018/01/01/core#inFacet" + ], + "base_uri": [ + "https://doi.org/10.29172" + ] + }, + { + "title": "Unified Astronomy Thesaurus (UAT)", + "preferredPrefix": "uat", + "description": "The Unified Astronomy Thesaurus (UAT) is an open, interoperable and community-supported thesaurus which unifies existing, divergent, and isolated controlled vocabularies in astronomy and astrophysics into a single high-quality, freely-available open thesaurus formalizing astronomical concepts and their inter-relationships. The UAT builds upon the IAU Thesaurus with major contributions from the Astronomy portions of the thesauri developed by the Institute of Physics Publishing and the American Institute of Physics. The Unified Astronomy Thesaurus will be further enhanced and updated through a collaborative effort involving broad community participation.", + "homepage": "http://astrothesaurus.org", + "tracker": null, + "logo": null, + "annotations": null, + "oboSlims": false, + "preferredRootTerms": [], + "allowDownload": false, + "classifications": null, + "license": null, + "repoUrl": null, + "uri": "https://raw.githubusercontent.com/astrothesaurus/UAT/master/UAT.rdf", + "id": "uat", + "mailing_list": "sio-ontology@googlegroups.com", + "ontology_purl": "https://raw.githubusercontent.com/astrothesaurus/UAT/master/UAT.rdf", + "reasoner": "NONE", + "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", + "definition_property": [ + "http://www.w3.org/2004/02/skos/core#definition" + ], + "synonym_property": [ + "http://www.w3.org/2004/02/skos/core#altLabel" + ], + "hierarchical_property": [ + "http://purl.obolibrary.org/obo/BFO_0000050" + ], + "base_uri": [ + "http://astrothesaurus.org/uat" + ] + }, + + + + + { + "title": "Simple Knowledge Organization System (SKOS) version of Materials Data Vocabulary", + "preferredPrefix": "mdv", + "description": "A version of the Materials Data Vocabulary structured as Simple Knowledge Organization System (SKOS). The XML was originally created by the TemaTres software. This vocabulary describes the applicability to material science of records in the NIST Materials Resource Registry (NMRR - https://materials.registry.nist.gov/). The NMRR allows for the registration of materials resources, bridging the gap between existing resources and the end users. The NMRR functions as a node in a federated system, making the registered information available for research to the materials community. This is being developed at the National Institute of Standards and Technology and is made available to solicit comments from the Material Science community. (An Excel version of the file is also included in the distributions for ease of use.) Please cite this resource as: Medina-Smith, Andrea; Becker, Chandler (2017), Simple Knowledge Organization System (SKOS) version of Materials Data Vocabulary , National Institute of Standards and Technology, https://doi.org/10.18434/T4/1435037", + "homepage": "https://data.nist.gov/od/id/67C783D4BA814C8EE05324570681708A1899", + "tracker": null, + "logo": null, + "annotations": null, + "oboSlims": false, + "preferredRootTerms": [], + "allowDownload": true, + "classifications": null, + "license": null, + "repoUrl": null, + "id": "mdv", + "ontology_purl": "https://data.nist.gov/od/dm/nmrr/vocab/", + "reasoner": "NONE", + "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", + "creator": [ + "Andrea Medina-Smith (https://orcid.org/0000-0002-1217-701X)", + "Chandler Becker (https://orcid.org/0000-0002-3653-0199)" + ], + "definition_property": [ + "http://www.w3.org/2004/02/skos/core#definition" + ], + "synonym_property": [ + "http://www.w3.org/2004/02/skos/core#altLabel" + ], + "hierarchical_property": [ + "http://purl.obolibrary.org/obo/BFO_0000050" + ], + "base_uri": [ + "https://data.nist.gov/od/dm/nmrr/vocab" + ] + } + ] +} + From 5db94efdd907fe240aa8d1ef005b26d7aed8a39c Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Wed, 3 Jan 2024 19:26:52 +0100 Subject: [PATCH 04/15] enabled hastopconcept option by making conceptSchema a term --- .../rdf2json/src/main/java/uk/ac/ebi/rdf2json/OntologyGraph.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dataload/rdf2json/src/main/java/uk/ac/ebi/rdf2json/OntologyGraph.java b/dataload/rdf2json/src/main/java/uk/ac/ebi/rdf2json/OntologyGraph.java index 275e6a511..c0b69a493 100644 --- a/dataload/rdf2json/src/main/java/uk/ac/ebi/rdf2json/OntologyGraph.java +++ b/dataload/rdf2json/src/main/java/uk/ac/ebi/rdf2json/OntologyGraph.java @@ -641,6 +641,7 @@ public void handleType(OntologyNode subjNode, Node type) { case "http://www.w3.org/2002/07/owl#Class": case "http://www.w3.org/2000/01/rdf-schema#Class": case "http://www.w3.org/2004/02/skos/core#Concept": + case "http://www.w3.org/2004/02/skos/core#ConceptScheme": subjNode.types.add(OntologyNode.NodeType.CLASS); if(subjNode.uri != null) { ++ numberOfClasses; From fb211c887c9d8bfa01ff3f371ba65fd891511257 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:45:26 +0100 Subject: [PATCH 05/15] implemented graph definition call for visualization --- .../v1/V1OntologySKOSConceptController.java | 179 ++++++++++++++---- .../controller/api/v1/V1SearchController.java | 2 - 2 files changed, 142 insertions(+), 39 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java index 6f2dd346a..f4dbb77c3 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java @@ -1,5 +1,8 @@ package uk.ac.ebi.spot.ols.controller.api.v1; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; @@ -27,8 +30,7 @@ import uk.ac.ebi.spot.ols.repository.v1.V1TermRepository; import javax.servlet.http.HttpServletRequest; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** * @author Simon Jupp @@ -47,7 +49,7 @@ public class V1OntologySKOSConceptController { @Autowired V1TermAssembler termAssembler; - + @Operation(description = "Get complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/conceptrelations/{iri} method with broader or narrower concept relations.") @RequestMapping(path = "/{onto}/concepthierarchy", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) HttpEntity>> getSKOSConceptHierarchyByOntology( @@ -67,8 +69,8 @@ HttpEntity>> getSKOSConceptHierarchyByOntology( return new ResponseEntity<>(termRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable), HttpStatus.OK); else return new ResponseEntity<>(termRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable), HttpStatus.OK); - } - + } + @Operation(description = "Display complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/displayconceptrelations/{iri} method with broader or narrower concept relations.") @RequestMapping(path = "/{onto}/displayconcepthierarchy", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody @@ -95,12 +97,12 @@ HttpEntity displaySKOSConceptHierarchyByOntology( StringBuilder sb = new StringBuilder(); for (TreeNode root : rootIndividuals) { sb.append(root.getIndex() + " , "+ root.getData().label + " , " + root.getData().iri).append("\n"); - sb.append(generateConceptHierarchyTextByOntology(root, displayRelated)); + sb.append(generateConceptHierarchyTextByOntology(root, displayRelated)); } - + return new HttpEntity(sb.toString()); - } - + } + @Operation(description = "Get partial SKOS concept hierarchy based on the encoded iri of the designated top concept") @RequestMapping(path = "/{onto}/concepthierarchy/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) HttpEntity> getSKOSConceptHierarchyByOntologyAndIri( @@ -121,11 +123,11 @@ HttpEntity> getSKOSConceptHierarchyByOntologyAndIri( decodedIri = UriUtils.decode(iri, "UTF-8"); topConcept = termRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); - if (topConcept.getData().iri == null) + if (topConcept.getData().iri == null) throw new ResourceNotFoundException("No roots could be found for " + ontologyId ); return new ResponseEntity<>(topConcept, HttpStatus.OK); - } - + } + @Operation(description = "Display partial SKOS concept hierarchy based on the encoded iri of the designated top concept") @RequestMapping(path = "/{onto}/displayconcepthierarchy/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody @@ -149,13 +151,13 @@ HttpEntity displaySKOSConceptHierarchyByOntologyAndIri( StringBuilder sb = new StringBuilder(); decodedIri = UriUtils.decode(iri, "UTF-8"); topConcept = termRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); - + sb.append(topConcept.getIndex() + " , "+ topConcept.getData().label + " , " + topConcept.getData().iri).append("\n"); - sb.append(generateConceptHierarchyTextByOntology(topConcept, displayRelated)); - + sb.append(generateConceptHierarchyTextByOntology(topConcept, displayRelated)); + return new HttpEntity(sb.toString()); - } - + } + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format.") @RequestMapping(path = "/{onto}/conceptrelations/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) public HttpEntity> findRelatedConcepts( @@ -164,13 +166,13 @@ public HttpEntity> findRelatedConcepts( @Parameter(description = "encoded concept IRI", required = true) @PathVariable("iri") String iri, @Parameter(description = "skos based concept relation type", required = true) - @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, Pageable pageable, PagedResourcesAssembler assembler) { - + ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); @@ -180,10 +182,63 @@ public HttpEntity> findRelatedConcepts( final int start = (int)pageable.getOffset(); final int end = Math.min((start + pageable.getPageSize()), related.size()); Page conceptPage = new PageImpl<>(related.subList(start, end), pageable, related.size()); - - return new ResponseEntity<>( assembler.toModel(conceptPage), HttpStatus.OK); + + return new ResponseEntity<>( assembler.toModel(conceptPage), HttpStatus.OK); } + + @Operation(description = "Node and Edge definitions needed to visualize the nodes that are directly related with the subject term. Ontology ID and encoded iri are required. ") + @RequestMapping(path = "/{onto}/graph/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + public HttpEntity retrieveImmediateGraph( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang){ + + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + + V1Term subjectTerm = termRepository.findByOntologyAndIri(ontologyId, decodedIri, lang); + + related = termRepository.findRelated(ontologyId, decodedIri, "related",lang); + + List narrower = new ArrayList(); + narrower = termRepository.findRelated(ontologyId, decodedIri, "narrower",lang); + + List broader = new ArrayList(); + broader = termRepository.findRelated(ontologyId, decodedIri, "broader",lang); + + Set relatedNodes = new HashSet(); + related.forEach(term -> relatedNodes.add(new Node(term.iri, term.label))); + Set narrowerNodes = new HashSet(); + narrower.forEach(term -> narrowerNodes.add(new Node(term.iri, term.label))); + Set broaderNodes = new HashSet(); + broader.forEach(term -> broaderNodes.add(new Node(term.iri, term.label))); + + Set edges = new HashSet(); + relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "related","http://www.w3.org/2004/02/skos/core#related"))); + narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "narrower","http://www.w3.org/2004/02/skos/core#narrower"))); + broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "broader","http://www.w3.org/2004/02/skos/core#broader"))); + + Set nodes = new HashSet(); + nodes.add(new Node(decodedIri,subjectTerm.label)); + nodes.addAll(relatedNodes); + nodes.addAll(broaderNodes); + nodes.addAll(narrowerNodes); + + + Map graph = new HashMap(); + graph.put("nodes", nodes); + graph.put("edges", edges); + ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + try { + return new ResponseEntity<>(ow.writeValueAsString(graph),HttpStatus.OK); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + @Operation(description = "Broader, Narrower and Related concept relations of a concept are displayed as text if the concept iri is provided in encoded format.") @RequestMapping(path = "/{onto}/displayconceptrelations/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody @@ -193,7 +248,7 @@ public HttpEntity displayRelatedConcepts( @Parameter(description = "encoded concept IRI", required = true) @PathVariable("iri") String iri, @Parameter(description = "skos based concept relation type", required = true) - @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, @@ -211,11 +266,11 @@ public HttpEntity displayRelatedConcepts( int count = 0; for (V1Term individual : conceptPage.getContent()) sb.append(++count).append(" , ").append(individual.label).append(" , ").append(individual.iri).append("\n"); - - return new HttpEntity<>( sb.toString()); + + return new HttpEntity<>( sb.toString()); } - + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format. The relationship is identified indirectly based on the related concept's relation to the concept in question. This requires traversing all the available concepts and checking if they are related to the concept in question. For this reason, this method is relatively slower than the displayconceptrelations method. Nevertheless, it enables to identify unforeseen relations of the concept in question") @RequestMapping(path = "/{onto}/conceptrelationsindirectly/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) public HttpEntity> findRelatedConceptsIndirectly( @@ -224,21 +279,21 @@ public HttpEntity> findRelatedConceptsIndirectly( @Parameter(description = "encoded concept IRI", required = true) @PathVariable("iri") String iri, @Parameter(description = "skos based concept relation type", required = true) - @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, Pageable pageable) { - + ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); related = termRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType, obsoletes,lang,pageable); - - return new ResponseEntity<>( related, HttpStatus.OK); + + return new ResponseEntity<>( related, HttpStatus.OK); } - + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format. The relationship is identified indirectly based on the related concept's relation to the concept in question. This requires traversing all the available concepts and checking if they are related to the concept in question. For this reason, this method is relatively slower than the displayconceptrelations method. Nevertheless, it enables to identify unforeseen relations of the concept in question") @RequestMapping(path = "/{onto}/displayconceptrelationsindirectly/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody @@ -248,7 +303,7 @@ public HttpEntity displayRelatedConceptsIndirectly( @Parameter(description = "encoded concept IRI", required = true) @PathVariable("iri") String iri, @Parameter(description = "skos based concept relation type", required = true) - @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, @Parameter(description = "Page size to retrieve individuals", required = true) @RequestParam(value = "obsoletes", required = false) Boolean obsoletes, @@ -259,16 +314,16 @@ public HttpEntity displayRelatedConceptsIndirectly( List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); related = termRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType, obsoletes,lang,pageable); - + int count = 0; for (V1Term individual : related) sb.append(++count).append(" , ").append(individual.label).append(" , ").append(individual.iri).append("\n"); - - return new ResponseEntity<>( sb.toString(), HttpStatus.OK); + + return new ResponseEntity<>( sb.toString(), HttpStatus.OK); } - + public StringBuilder generateConceptHierarchyTextByOntology(TreeNode rootConcept, boolean displayRelated) { StringBuilder sb = new StringBuilder(); for (TreeNode childConcept : rootConcept.getChildren()) { @@ -282,7 +337,7 @@ public StringBuilder generateConceptHierarchyTextByOntology(TreeNode roo } return sb; } - + @RequestMapping(method = RequestMethod.GET, produces = {MediaType.TEXT_PLAIN_VALUE}, value = "/removeConceptTreeCache") public HttpEntity removeConceptTreeCache() { return new HttpEntity(termRepository.removeConceptTreeCache()); @@ -291,6 +346,56 @@ public HttpEntity removeConceptTreeCache() { @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found") @ExceptionHandler(ResourceNotFoundException.class) public void handleError(HttpServletRequest req, Exception exception) { - } + } + + public class Node { + String iri; + String label; + + public Node(String iri, String label) { + this.iri = iri; + this.label = label; + } + + public String getIri() { + return iri; + } + + public String getLabel() { + return label; + } + + } + + public class Edge { + String source; + String target; + String label; + String uri; + + public Edge(String source, String target, String label, String uri) { + this.source = source; + this.target = target; + this.label = label; + this.uri = uri; + } + + public String getSource() { + return source; + } + + public String getTarget() { + return target; + } + + public String getLabel() { + return label; + } + + public String getUri() { + return uri; + } + + } } diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java index e4c0273a0..a55598567 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1SearchController.java @@ -5,12 +5,10 @@ import com.google.gson.JsonParser; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; -import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import uk.ac.ebi.spot.ols.repository.Validation; import uk.ac.ebi.spot.ols.repository.solr.OlsSolrClient; From d2df44f8ee3c7426c743dde213e8242e558308aa Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:35:39 +0100 Subject: [PATCH 06/15] removed yet to be introduced caching for now and moved graph method below --- .../v1/V1OntologySKOSConceptController.java | 109 +++++++++--------- .../ols/repository/v1/V1TermRepository.java | 106 ++++++++--------- 2 files changed, 102 insertions(+), 113 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java index f4dbb77c3..c30ea350f 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java @@ -187,58 +187,6 @@ public HttpEntity> findRelatedConcepts( } - @Operation(description = "Node and Edge definitions needed to visualize the nodes that are directly related with the subject term. Ontology ID and encoded iri are required. ") - @RequestMapping(path = "/{onto}/graph/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) - public HttpEntity retrieveImmediateGraph( - @Parameter(description = "ontology ID", required = true) - @PathVariable("onto") String ontologyId, - @Parameter(description = "encoded concept IRI", required = true) - @PathVariable("iri") String iri, - @RequestParam(value = "lang", required = false, defaultValue = "en") String lang){ - - List related = new ArrayList(); - String decodedIri = UriUtils.decode(iri, "UTF-8"); - - V1Term subjectTerm = termRepository.findByOntologyAndIri(ontologyId, decodedIri, lang); - - related = termRepository.findRelated(ontologyId, decodedIri, "related",lang); - - List narrower = new ArrayList(); - narrower = termRepository.findRelated(ontologyId, decodedIri, "narrower",lang); - - List broader = new ArrayList(); - broader = termRepository.findRelated(ontologyId, decodedIri, "broader",lang); - - Set relatedNodes = new HashSet(); - related.forEach(term -> relatedNodes.add(new Node(term.iri, term.label))); - Set narrowerNodes = new HashSet(); - narrower.forEach(term -> narrowerNodes.add(new Node(term.iri, term.label))); - Set broaderNodes = new HashSet(); - broader.forEach(term -> broaderNodes.add(new Node(term.iri, term.label))); - - Set edges = new HashSet(); - relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "related","http://www.w3.org/2004/02/skos/core#related"))); - narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "narrower","http://www.w3.org/2004/02/skos/core#narrower"))); - broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "broader","http://www.w3.org/2004/02/skos/core#broader"))); - - Set nodes = new HashSet(); - nodes.add(new Node(decodedIri,subjectTerm.label)); - nodes.addAll(relatedNodes); - nodes.addAll(broaderNodes); - nodes.addAll(narrowerNodes); - - - Map graph = new HashMap(); - graph.put("nodes", nodes); - graph.put("edges", edges); - ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); - try { - return new ResponseEntity<>(ow.writeValueAsString(graph),HttpStatus.OK); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - @Operation(description = "Broader, Narrower and Related concept relations of a concept are displayed as text if the concept iri is provided in encoded format.") @RequestMapping(path = "/{onto}/displayconceptrelations/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody @@ -324,6 +272,58 @@ public HttpEntity displayRelatedConceptsIndirectly( } + @Operation(description = "Node and Edge definitions needed to visualize the nodes that are directly related with the subject term. Ontology ID and encoded iri are required. ") + @RequestMapping(path = "/{onto}/graph/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + public HttpEntity retrieveImmediateGraph( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang){ + + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + + V1Term subjectTerm = termRepository.findByOntologyAndIri(ontologyId, decodedIri, lang); + + related = termRepository.findRelated(ontologyId, decodedIri, "related",lang); + + List narrower = new ArrayList(); + narrower = termRepository.findRelated(ontologyId, decodedIri, "narrower",lang); + + List broader = new ArrayList(); + broader = termRepository.findRelated(ontologyId, decodedIri, "broader",lang); + + Set relatedNodes = new HashSet(); + related.forEach(term -> relatedNodes.add(new Node(term.iri, term.label))); + Set narrowerNodes = new HashSet(); + narrower.forEach(term -> narrowerNodes.add(new Node(term.iri, term.label))); + Set broaderNodes = new HashSet(); + broader.forEach(term -> broaderNodes.add(new Node(term.iri, term.label))); + + Set edges = new HashSet(); + relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "related","http://www.w3.org/2004/02/skos/core#related"))); + narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "narrower","http://www.w3.org/2004/02/skos/core#narrower"))); + broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "broader","http://www.w3.org/2004/02/skos/core#broader"))); + + Set nodes = new HashSet(); + nodes.add(new Node(decodedIri,subjectTerm.label)); + nodes.addAll(relatedNodes); + nodes.addAll(broaderNodes); + nodes.addAll(narrowerNodes); + + + Map graph = new HashMap(); + graph.put("nodes", nodes); + graph.put("edges", edges); + ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + try { + return new ResponseEntity<>(ow.writeValueAsString(graph),HttpStatus.OK); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + public StringBuilder generateConceptHierarchyTextByOntology(TreeNode rootConcept, boolean displayRelated) { StringBuilder sb = new StringBuilder(); for (TreeNode childConcept : rootConcept.getChildren()) { @@ -338,11 +338,6 @@ public StringBuilder generateConceptHierarchyTextByOntology(TreeNode roo return sb; } - @RequestMapping(method = RequestMethod.GET, produces = {MediaType.TEXT_PLAIN_VALUE}, value = "/removeConceptTreeCache") - public HttpEntity removeConceptTreeCache() { - return new HttpEntity(termRepository.removeConceptTreeCache()); - } - @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found") @ExceptionHandler(ResourceNotFoundException.class) public void handleError(HttpServletRequest req, Exception exception) { diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java index ec078c81d..3a34480e2 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java @@ -297,23 +297,23 @@ public Page findAllByOboIdAndIsDefiningOntology(String oboId, String lan public Page getInstances(String ontologyId, String iri, Pageable pageable) { throw new RuntimeException(); } - - - - @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#schema).concat('-').concat(#narrower).concat('-').concat(#withChildren)") + + + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#schema).concat('-').concat(#narrower).concat('-').concat(#withChildren)") public List> conceptTree (String ontologyId, boolean schema, boolean narrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable){ Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); List listOfTerms = new ArrayList(); - listOfTerms.addAll(terms.getContent()); - + listOfTerms.addAll(terms.getContent()); + while(terms.hasNext()) { terms = this.findAllByOntology(ontologyId, obsoletes, lang, terms.nextPageable()); listOfTerms.addAll(terms.getContent()); - } - - List> rootIndividuals = new ArrayList>(); + } + + List> rootIndividuals = new ArrayList>(); int count = 0; - + if(schema) { for (V1Term indiv : listOfTerms) if (indiv.annotation.get("hasTopConcept") != null) { @@ -329,10 +329,10 @@ public List> conceptTree (String ontologyId, boolean schema, bo } rootIndividuals.add(topConcept); } - } + } } else for (V1Term individual : listOfTerms) { TreeNode tree = new TreeNode(individual); - + if (tree.isRoot() && individual.annotation.get("topConceptOf") != null) { tree.setIndex(String.valueOf(++count)); if(withChildren) { @@ -343,22 +343,22 @@ public List> conceptTree (String ontologyId, boolean schema, bo } rootIndividuals.add(tree); } - } - + } + return rootIndividuals; } - + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#narrower).concat('-').concat(#withChildren)") public List> conceptTreeWithoutTop (String ontologyId, boolean narrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable){ Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); List listOfTerms = new ArrayList(); - listOfTerms.addAll(terms.getContent()); - + listOfTerms.addAll(terms.getContent()); + while(terms.hasNext()) { terms = this.findAllByOntology(ontologyId, obsoletes, lang, terms.nextPageable()); listOfTerms.addAll(terms.getContent()); - } - + } + Set rootIRIs = new HashSet(); List> rootIndividuals = new ArrayList>(); int count = 0; @@ -369,11 +369,11 @@ public List> conceptTreeWithoutTop (String ontologyId, boolean V1Term broaderIndividual = findIndividual(listOfTerms,iriBroader); if (broaderIndividual.annotation.get("broader") == null) { rootIRIs.add(iriBroader); - } + } } } } - + for (String iri : rootIRIs) { V1Term topConceptIndividual = findIndividual(listOfTerms, iri); TreeNode topConcept = new TreeNode(topConceptIndividual); @@ -382,7 +382,7 @@ public List> conceptTreeWithoutTop (String ontologyId, boolean populateChildrenandRelatedByBroader(topConceptIndividual,topConcept,listOfTerms); rootIndividuals.add(topConcept); } - + } else { for (V1Term individual : listOfTerms) { if (individual.annotation.get("narrower") != null) { @@ -393,9 +393,9 @@ public List> conceptTreeWithoutTop (String ontologyId, boolean if (individual.iri.equals(iriNarrower)) root = false; } - } + } } - + if(root) { TreeNode topConcept = new TreeNode(individual); topConcept.setIndex(String.valueOf(++count)); @@ -406,22 +406,22 @@ public List> conceptTreeWithoutTop (String ontologyId, boolean } } } - + return rootIndividuals; } - + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat('s').concat('-').concat(#iri).concat('-').concat(#narrower).concat('-').concat(#index)") public TreeNode conceptSubTree(String ontologyId, String iri, boolean narrower, String index, Boolean obsoletes, String lang, Pageable pageable){ Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); List listOfTerms = new ArrayList(); - listOfTerms.addAll(terms.getContent()); - + listOfTerms.addAll(terms.getContent()); + while(terms.hasNext()) { terms = this.findAllByOntology(ontologyId, obsoletes, lang, terms.nextPageable()); listOfTerms.addAll(terms.getContent()); } - V1Term topConceptIndividual = findIndividual(listOfTerms,iri); + V1Term topConceptIndividual = findIndividual(listOfTerms,iri); TreeNode topConcept = new TreeNode(topConceptIndividual); topConcept.setIndex(index); if(narrower) @@ -431,56 +431,56 @@ public TreeNode conceptSubTree(String ontologyId, String iri, boolean na return topConcept; } - + public V1Term findIndividual(List wholeList, String iri) { for (V1Term individual : wholeList) if(individual.iri.equals(iri)) return individual; return new V1Term(); } - + public List findRelated(String ontologyId, String iri, String relationType, String lang) { - List related = new ArrayList(); + List related = new ArrayList(); V1Term individual = this.findByOntologyAndIri(ontologyId, iri, lang); if (individual != null) if (individual.annotation.get(relationType) != null) - for (String iriBroader : (LinkedHashSet) individual.annotation.get(relationType)) + for (String iriBroader : (LinkedHashSet) individual.annotation.get(relationType)) related.add(this.findByOntologyAndIri(ontologyId, iriBroader, lang)); - + return related; } - + public ListfindRelatedIndirectly(String ontologyId, String iri, String relationType, Boolean obsoletes, String lang, Pageable pageable){ - List related = new ArrayList(); - + List related = new ArrayList(); + V1Term individual = this.findByOntologyAndIri(ontologyId, iri, lang); if(individual == null) return related; if(individual.iri == null) return related; - + Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); List listOfTerms = new ArrayList(); - listOfTerms.addAll(terms.getContent()); - + listOfTerms.addAll(terms.getContent()); + while(terms.hasNext()) { terms = this.findAllByOntology(ontologyId, obsoletes, lang, terms.nextPageable()); listOfTerms.addAll(terms.getContent()); - } - + } + for (V1Term term : listOfTerms) { if (term != null) if (term.annotation.get(relationType) != null) - for (String iriRelated : (LinkedHashSet) term.annotation.get(relationType)) + for (String iriRelated : (LinkedHashSet) term.annotation.get(relationType)) if(iriRelated.equals(iri)) related.add(term); } - + return related; } - + public void populateChildrenandRelatedByNarrower(V1Term individual, TreeNode tree, List listOfTerms ) { - + if (individual.annotation.get("related") != null) for (String iriRelated : (LinkedHashSet) individual.annotation.get("related")) { TreeNode related = new TreeNode(findIndividual(listOfTerms,iriRelated)); @@ -492,12 +492,12 @@ public void populateChildrenandRelatedByNarrower(V1Term individual, TreeNode) individual.annotation.get("narrower")) { V1Term childIndividual = findIndividual(listOfTerms,iriChild); TreeNode child = new TreeNode(childIndividual); - child.setIndex(tree.getIndex()+"."+ ++count); + child.setIndex(tree.getIndex()+"."+ ++count); populateChildrenandRelatedByNarrower(childIndividual,child,listOfTerms); tree.addChild(child); } } - + public void populateChildrenandRelatedByBroader(V1Term individual, TreeNode tree, List listOfTerms) { if (individual.annotation.get("related") != null) for (String iriRelated : (LinkedHashSet) individual.annotation.get("related")) { @@ -512,16 +512,10 @@ public void populateChildrenandRelatedByBroader(V1Term individual, TreeNode child = new TreeNode(indiv); - child.setIndex(tree.getIndex()+"."+ ++count); + child.setIndex(tree.getIndex()+"."+ ++count); populateChildrenandRelatedByBroader(indiv,child,listOfTerms); tree.addChild(child); - } + } } } - - @CacheEvict(value="concepttree", allEntries=true) - public String removeConceptTreeCache() { - return "All concept tree cache removed!"; - } - } From c7f8de2bd6c973658c06e21d429bd536c78ade3a Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:20:48 +0100 Subject: [PATCH 07/15] renamed call signatures with respect to EBI conventions in #625 --- .../v1/V1OntologySKOSConceptController.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java index c30ea350f..44415e81c 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java @@ -51,7 +51,7 @@ public class V1OntologySKOSConceptController { V1TermAssembler termAssembler; @Operation(description = "Get complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/conceptrelations/{iri} method with broader or narrower concept relations.") - @RequestMapping(path = "/{onto}/concepthierarchy", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/tree", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) HttpEntity>> getSKOSConceptHierarchyByOntology( @Parameter(description = "ontology ID", required = true) @PathVariable("onto") String ontologyId, @@ -72,7 +72,7 @@ HttpEntity>> getSKOSConceptHierarchyByOntology( } @Operation(description = "Display complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/displayconceptrelations/{iri} method with broader or narrower concept relations.") - @RequestMapping(path = "/{onto}/displayconcepthierarchy", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/displaytree", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody HttpEntity displaySKOSConceptHierarchyByOntology( @Parameter(description = "ontology ID", required = true) @@ -104,7 +104,7 @@ HttpEntity displaySKOSConceptHierarchyByOntology( } @Operation(description = "Get partial SKOS concept hierarchy based on the encoded iri of the designated top concept") - @RequestMapping(path = "/{onto}/concepthierarchy/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/{iri}/tree", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) HttpEntity> getSKOSConceptHierarchyByOntologyAndIri( @Parameter(description = "ontology ID", required = true) @PathVariable("onto") String ontologyId, @@ -129,7 +129,7 @@ HttpEntity> getSKOSConceptHierarchyByOntologyAndIri( } @Operation(description = "Display partial SKOS concept hierarchy based on the encoded iri of the designated top concept") - @RequestMapping(path = "/{onto}/displayconcepthierarchy/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/{iri}/displaytree", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody HttpEntity displaySKOSConceptHierarchyByOntologyAndIri( @Parameter(description = "ontology ID", required = true) @@ -159,7 +159,7 @@ HttpEntity displaySKOSConceptHierarchyByOntologyAndIri( } @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format.") - @RequestMapping(path = "/{onto}/conceptrelations/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/{iri}/relations", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) public HttpEntity> findRelatedConcepts( @Parameter(description = "ontology ID", required = true) @PathVariable("onto") String ontologyId, @@ -188,7 +188,7 @@ public HttpEntity> findRelatedConcepts( } @Operation(description = "Broader, Narrower and Related concept relations of a concept are displayed as text if the concept iri is provided in encoded format.") - @RequestMapping(path = "/{onto}/displayconceptrelations/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/{iri}/displayrelations", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody public HttpEntity displayRelatedConcepts( @Parameter(description = "ontology ID", required = true) @@ -220,7 +220,7 @@ public HttpEntity displayRelatedConcepts( } @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format. The relationship is identified indirectly based on the related concept's relation to the concept in question. This requires traversing all the available concepts and checking if they are related to the concept in question. For this reason, this method is relatively slower than the displayconceptrelations method. Nevertheless, it enables to identify unforeseen relations of the concept in question") - @RequestMapping(path = "/{onto}/conceptrelationsindirectly/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/{iri}/indirectrelations", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) public HttpEntity> findRelatedConceptsIndirectly( @Parameter(description = "ontology ID", required = true) @PathVariable("onto") String ontologyId, @@ -243,7 +243,7 @@ public HttpEntity> findRelatedConceptsIndirectly( } @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format. The relationship is identified indirectly based on the related concept's relation to the concept in question. This requires traversing all the available concepts and checking if they are related to the concept in question. For this reason, this method is relatively slower than the displayconceptrelations method. Nevertheless, it enables to identify unforeseen relations of the concept in question") - @RequestMapping(path = "/{onto}/displayconceptrelationsindirectly/{iri}", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/{iri}/displayindirectrelations", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) @ResponseBody public HttpEntity displayRelatedConceptsIndirectly( @Parameter(description = "ontology ID", required = true) @@ -273,7 +273,7 @@ public HttpEntity displayRelatedConceptsIndirectly( } @Operation(description = "Node and Edge definitions needed to visualize the nodes that are directly related with the subject term. Ontology ID and encoded iri are required. ") - @RequestMapping(path = "/{onto}/graph/{iri}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + @RequestMapping(path = "/{onto}/skos/{iri}/graph", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) public HttpEntity retrieveImmediateGraph( @Parameter(description = "ontology ID", required = true) @PathVariable("onto") String ontologyId, From 5080ff81432e664f0eb74d097e8737e9da232279 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Mon, 19 Feb 2024 17:44:13 +0100 Subject: [PATCH 08/15] handled null relations for some ontologies in EBISPOT#625 and TIBHannover#1 --- .../ols/repository/v1/V1TermRepository.java | 44 ++-- dataload/configs/skos_ontologies.json | 237 ++++++++++-------- 2 files changed, 153 insertions(+), 128 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java index 3a34480e2..3b8e72246 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java @@ -481,34 +481,34 @@ public List findRelated(String ontologyId, String iri, String relationTy public void populateChildrenandRelatedByNarrower(V1Term individual, TreeNode tree, List listOfTerms ) { - if (individual.annotation.get("related") != null) - for (String iriRelated : (LinkedHashSet) individual.annotation.get("related")) { - TreeNode related = new TreeNode(findIndividual(listOfTerms,iriRelated)); - related.setIndex(tree.getIndex()+ ".related"); - tree.addRelated(related); - } + if (individual.annotation != null) + for (String iriRelated : (LinkedHashSet) individual.annotation.getOrDefault("related", new LinkedHashSet())) { + TreeNode related = new TreeNode(findIndividual(listOfTerms, iriRelated)); + related.setIndex(tree.getIndex() + ".related"); + tree.addRelated(related); + } int count = 0; - if (individual.annotation.get("narrower") != null) - for (String iriChild : (LinkedHashSet) individual.annotation.get("narrower")) { - V1Term childIndividual = findIndividual(listOfTerms,iriChild); - TreeNode child = new TreeNode(childIndividual); - child.setIndex(tree.getIndex()+"."+ ++count); - populateChildrenandRelatedByNarrower(childIndividual,child,listOfTerms); - tree.addChild(child); - } + if (individual.annotation != null) + for (String iriChild : (LinkedHashSet) individual.annotation.getOrDefault("narrower", new LinkedHashSet())) { + V1Term childIndividual = findIndividual(listOfTerms, iriChild); + TreeNode child = new TreeNode(childIndividual); + child.setIndex(tree.getIndex() + "." + ++count); + populateChildrenandRelatedByNarrower(childIndividual, child, listOfTerms); + tree.addChild(child); + } } public void populateChildrenandRelatedByBroader(V1Term individual, TreeNode tree, List listOfTerms) { - if (individual.annotation.get("related") != null) - for (String iriRelated : (LinkedHashSet) individual.annotation.get("related")) { - TreeNode related = new TreeNode(findIndividual(listOfTerms,iriRelated)); - related.setIndex(tree.getIndex()+ ".related"); - tree.addRelated(related); - } + if (individual.annotation != null) + for (String iriRelated : (LinkedHashSet) individual.annotation.getOrDefault("related", new LinkedHashSet())) { + TreeNode related = new TreeNode(findIndividual(listOfTerms, iriRelated)); + related.setIndex(tree.getIndex() + ".related"); + tree.addRelated(related); + } int count = 0; for ( V1Term indiv : listOfTerms) { - if (indiv.annotation.get("broader") != null) - for (String iriBroader : (LinkedHashSet) indiv.annotation.get("broader")) + if (indiv.annotation != null) + for (String iriBroader : (LinkedHashSet) indiv.annotation.getOrDefault("broader",new LinkedHashSet())) if(individual.iri != null) if (individual.iri.equals(iriBroader)) { TreeNode child = new TreeNode(indiv); diff --git a/dataload/configs/skos_ontologies.json b/dataload/configs/skos_ontologies.json index 403f54325..37afcda80 100644 --- a/dataload/configs/skos_ontologies.json +++ b/dataload/configs/skos_ontologies.json @@ -1,113 +1,138 @@ { "ontologies": [ { - "title": "PhySH - Physics Subject Headings", - "preferredPrefix": "physh", - "description": "PhySH (Physics Subject Headings) is a physics classification scheme developed by APS to organize journal, meeting, and other content by topic.", - "homepage": "https://physh.org/", - "tracker": null, - "logo": null, - "annotations": null, - "oboSlims": false, - "preferredRootTerms": [], - "allowDownload": false, - "classifications": null, - "license": null, - "repoUrl": null, - "uri": "https://raw.githubusercontent.com/physh-org/PhySH/master/physh.ttl", - "id": "physh", - "mailing_list": null, - "ontology_purl": "https://raw.githubusercontent.com/physh-org/PhySH/master/physh.ttl", - "reasoner": "NONE", - "label_property": "https://physh.org/rdf/2018/01/01/core#prefLabel", - "definition_property": [ - "http://www.w3.org/2004/02/skos/core#definition", - "http://purl.org/dc/terms/description" - ], - "synonym_property": [ - "http://www.w3.org/2004/02/skos/core#altLabel" - ], - "hierarchical_property": [ - "http://www.w3.org/2004/02/skos/core#broader", - "https://physh.org/rdf/2018/01/01/core#inDiscipline", - "https://physh.org/rdf/2018/01/01/core#inFacet" - ], - "base_uri": [ - "https://doi.org/10.29172" - ] - }, + "ontology_purl": "https://raw.githubusercontent.com/physh-org/PhySH/master/physh.ttl", + "description": "PhySH (Physics Subject Headings) is a physics classification scheme developed by APS to organize journal, meeting, and other content by topic.", + "homepage": "https://physh.org/", + "id": "PhySH", + "license": { + "label": "CC-0 1.0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/" + }, + "title": "PhySH - Physics Subject Headings", + "tracker": "https://github.com/physh-org/PhySH/issues", + "definition_property": [ + "http://www.w3.org/2004/02/skos/core#definition", + "http://purl.org/dc/terms/description" + ], + "creator": [ + "American Physical Society (https://www.aps.org/)" + ], + "preferredPrefix": "physh", + "hierarchical_property": [ + "http://www.w3.org/2004/02/skos/core#broader", + "https://physh.org/rdf/2018/01/01/core#inDiscipline", + "https://physh.org/rdf/2018/01/01/core#inFacet" + ], + "label_property": "https://physh.org/rdf/2018/01/01/core#prefLabel", + "synonym_property": [ + "http://www.w3.org/2004/02/skos/core#altLabel" + ], + "base_uri": [ + "https://doi.org/10.29172" + ], + "repo_url": "https://github.com/physh-org/PhySH", + "skos": true +}, { - "title": "Unified Astronomy Thesaurus (UAT)", - "preferredPrefix": "uat", - "description": "The Unified Astronomy Thesaurus (UAT) is an open, interoperable and community-supported thesaurus which unifies existing, divergent, and isolated controlled vocabularies in astronomy and astrophysics into a single high-quality, freely-available open thesaurus formalizing astronomical concepts and their inter-relationships. The UAT builds upon the IAU Thesaurus with major contributions from the Astronomy portions of the thesauri developed by the Institute of Physics Publishing and the American Institute of Physics. The Unified Astronomy Thesaurus will be further enhanced and updated through a collaborative effort involving broad community participation.", - "homepage": "http://astrothesaurus.org", - "tracker": null, - "logo": null, - "annotations": null, - "oboSlims": false, - "preferredRootTerms": [], - "allowDownload": false, - "classifications": null, - "license": null, - "repoUrl": null, - "uri": "https://raw.githubusercontent.com/astrothesaurus/UAT/master/UAT.rdf", - "id": "uat", - "mailing_list": "sio-ontology@googlegroups.com", - "ontology_purl": "https://raw.githubusercontent.com/astrothesaurus/UAT/master/UAT.rdf", - "reasoner": "NONE", - "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", - "definition_property": [ - "http://www.w3.org/2004/02/skos/core#definition" - ], - "synonym_property": [ - "http://www.w3.org/2004/02/skos/core#altLabel" - ], - "hierarchical_property": [ - "http://purl.obolibrary.org/obo/BFO_0000050" - ], - "base_uri": [ - "http://astrothesaurus.org/uat" - ] - }, - - - - - { - "title": "Simple Knowledge Organization System (SKOS) version of Materials Data Vocabulary", - "preferredPrefix": "mdv", - "description": "A version of the Materials Data Vocabulary structured as Simple Knowledge Organization System (SKOS). The XML was originally created by the TemaTres software. This vocabulary describes the applicability to material science of records in the NIST Materials Resource Registry (NMRR - https://materials.registry.nist.gov/). The NMRR allows for the registration of materials resources, bridging the gap between existing resources and the end users. The NMRR functions as a node in a federated system, making the registered information available for research to the materials community. This is being developed at the National Institute of Standards and Technology and is made available to solicit comments from the Material Science community. (An Excel version of the file is also included in the distributions for ease of use.) Please cite this resource as: Medina-Smith, Andrea; Becker, Chandler (2017), Simple Knowledge Organization System (SKOS) version of Materials Data Vocabulary , National Institute of Standards and Technology, https://doi.org/10.18434/T4/1435037", - "homepage": "https://data.nist.gov/od/id/67C783D4BA814C8EE05324570681708A1899", - "tracker": null, - "logo": null, - "annotations": null, - "oboSlims": false, - "preferredRootTerms": [], - "allowDownload": true, - "classifications": null, - "license": null, - "repoUrl": null, - "id": "mdv", - "ontology_purl": "https://data.nist.gov/od/dm/nmrr/vocab/", - "reasoner": "NONE", - "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", - "creator": [ - "Andrea Medina-Smith (https://orcid.org/0000-0002-1217-701X)", - "Chandler Becker (https://orcid.org/0000-0002-3653-0199)" - ], - "definition_property": [ - "http://www.w3.org/2004/02/skos/core#definition" - ], - "synonym_property": [ - "http://www.w3.org/2004/02/skos/core#altLabel" - ], - "hierarchical_property": [ - "http://purl.obolibrary.org/obo/BFO_0000050" - ], - "base_uri": [ - "https://data.nist.gov/od/dm/nmrr/vocab" - ] - } + "ontology_purl": "https://raw.githubusercontent.com/astrothesaurus/UAT/master/UAT.rdf", + "title": "Unified Astronomy Thesaurus (UAT)", + "id": "uat", + "preferredPrefix": "uat", + "license": { + "label": "Creative Commons Attribution-ShareAlike 3.0 Unported License", + "url": "https://github.com/astrothesaurus/UAT/blob/master/LICENSE.md" + }, + "mailing_list": "sio-ontology@googlegroups.com", + "description": "The Unified Astronomy Thesaurus (UAT) is an open, interoperable and community-supported thesaurus which unifies existing, divergent, and isolated controlled vocabularies in astronomy and astrophysics into a single high-quality, freely-available open thesaurus formalizing astronomical concepts and their inter-relationships. The UAT builds upon the IAU Thesaurus with major contributions from the Astronomy portions of the thesauri developed by the Institute of Physics Publishing and the American Institute of Physics. The Unified Astronomy Thesaurus will be further enhanced and updated through a collaborative effort involving broad community participation.", + "homepage": "http://astrothesaurus.org", + "creator": [ + "Frey Katie" + ], + "is_foundary": false, + "tracker": "https://github.com/astrothesaurus/UAT/issues", + "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", + "base_uri": [ + "http://astrothesaurus.org/uat" + ], + "synonym_property": [ + "http://www.w3.org/2004/02/skos/core#altLabel" + ], + "definition_property": [ + "http://www.w3.org/2004/02/skos/core#definition" + ], + "repo_url": "https://github.com/astrothesaurus/UAT", + "skos": true +}, +{ + "ontology_purl": "https://raw.githubusercontent.com/dini-ag-kim/hochschulfaechersystematik/master/hochschulfaechersystematik.ttl", + "id": "hsfs", + "title": "Hochschulfächersystematik", + "description": "Diese Hochschulfächersystematik basiert auf der Destatis-Systematik der Fächergruppen, Studienbereiche und Studienfächer (http://bartoc.org/node/18919) und wird gepflegt von der OER-Metadatengruppe der DINI-AG KIM. Die Systematik ist Bestandteil der Spezifikationen LOM for Higher Education OER Repositories und LRMI-Profil (Entwurf).", + "repo_url": "https://github.com/dini-ag-kim/hochschulfaechersystematik", + "preferredPrefix": "hsfs", + "allow_download": true, + "homepage": "https://bartoc.org/en/node/18919", + "base_uri": [ + "https://w3id.org/kim/hochschulfaechersystematik/" + ], + "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", + "skos": true, + "creator": [ + "Michael Menzel", + "Adrian Pohl" + ], + "license": { + "label": "freely available", + "url": "http://bartoc.org/en/Access/Free" + } +}, +{ + "ontology_purl": "https://purl.org/fidbaudigital/subjects", + "title": "FID BAUdigital Subject Headings", + "id": "bdsubj", + "preferredPrefix": "bdsubj", + "license": { + "label": "CC-BY 4.0", + "url": "https://creativecommons.org/licenses/by/4.0/" + }, + "description": "This subject heading system has beeen developed for use in FID BAUdigital and its future web services. It covers scientific fields of Civil Engineering, Architecture and Urban Studies with a special section on digitization. This subject classification has been mapped to several other classification systems. The latest version of the subject classification including these mappings is available at https://gitlab.com/fid-bau/terminologie/fid-baudigital-faecherklassifikation/-/raw/main/Subject_Headings_all_mappings.owl.", + "homepage": "https://gitlab.com/fid-bau/terminologie/fid-baudigital-faecherklassifikation", + "tracker": "https://gitlab.com/fid-bau/terminologie/fid-baudigital-faecherklassifikation/-/issues", + "definition_property": [ + "http://www.w3.org/2004/02/skos/core#definition" + ], + "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", + "creator": [ + "Fraunhofer-Informationszentrum Raum und Bau IRB" + ], + "base_uri": [ + "https://purl.org/fidbaudigital/subjects" + ], + "skos": true, + "repo_url": "https://gitlab.com/fid-bau/terminologie/fid-baudigital-faecherklassifikation" +}, +{ + "ontology_purl": "https://vocabs-downloads.acdh.oeaw.ac.at/vocabs-main/GeneralConcepts/OeFOS/oefos_disciplines.ttl", + "id": "oefos", + "license": { + "label": "Creative Commons Attribution 4.0 International License.", + "url": "https://creativecommons.org/licenses/by/4.0/" + }, + "title": "The Austrian Fields of Science and Technology Classification (ÖFOS 2012)", + "description": "The Austrian Fields of Science and Technology Classification (ÖFOS 2012) is the Austrian version of the revised international Fields of Science and Technology Classification of the OECD (FOS) published in the Frascati Manual 2015 as Fields of Research and Development (FORD). These fields are adjusted to national needs, whose application for international comparisons is binding, particularly within the European Statistical System. The six major Fields of Science: Natural Sciences; Technical Sciences; Human Medicine, Health Sciences; Agricultural Sciences, Veterinary Medicine; Social Sciences and Humanities remained unchanged in comparison to ÖFOS 2002. In order to ensure international comparability, the previous 2-digit levels from 2002, which are no longer applicable, were replaced by new 3-digit levels (groups) according to the international FOS respectively FORD. These 3-digit levels were provided with further sub-groups (4-digits) taking into account the comments of the international classification. It is therefore feasible that the new Austrian Fields of Science adapt to national peculiarities of the Austrian research activities. The research area with the corresponding 6-digits in alphabetical order serves as a description of the fields of activities and research projects and/or for the coverage of the main scientific activities of a statistical unit in the research and development surveys. (Current revision status: August 2017)", + "homepage": "https://vocabs.dariah.eu/oefos/en/", + "base_uri": [ + "https://vocabs.acdh.oeaw.ac.at/oefosdisciplines/" + ], + "allow_download": true, + "preferredPrefix": "oefos", + "skos": true, + "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", + "creator": [ + "Christoph Hoffmann" + ] +} ] } From c249078582a83562bd2a304fb18d17e3aafce456 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Tue, 20 Feb 2024 15:58:30 +0100 Subject: [PATCH 09/15] changed skos variable name in the config file --- dataload/configs/skos_ontologies.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dataload/configs/skos_ontologies.json b/dataload/configs/skos_ontologies.json index 37afcda80..6aa93a543 100644 --- a/dataload/configs/skos_ontologies.json +++ b/dataload/configs/skos_ontologies.json @@ -32,7 +32,7 @@ "https://doi.org/10.29172" ], "repo_url": "https://github.com/physh-org/PhySH", - "skos": true + "isSkos": true }, { "ontology_purl": "https://raw.githubusercontent.com/astrothesaurus/UAT/master/UAT.rdf", @@ -62,7 +62,7 @@ "http://www.w3.org/2004/02/skos/core#definition" ], "repo_url": "https://github.com/astrothesaurus/UAT", - "skos": true + "isSkos": true }, { "ontology_purl": "https://raw.githubusercontent.com/dini-ag-kim/hochschulfaechersystematik/master/hochschulfaechersystematik.ttl", @@ -77,7 +77,7 @@ "https://w3id.org/kim/hochschulfaechersystematik/" ], "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", - "skos": true, + "isSkos": true, "creator": [ "Michael Menzel", "Adrian Pohl" @@ -109,7 +109,7 @@ "base_uri": [ "https://purl.org/fidbaudigital/subjects" ], - "skos": true, + "isSkos": true, "repo_url": "https://gitlab.com/fid-bau/terminologie/fid-baudigital-faecherklassifikation" }, { @@ -127,7 +127,7 @@ ], "allow_download": true, "preferredPrefix": "oefos", - "skos": true, + "isSkos": true, "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", "creator": [ "Christoph Hoffmann" From 0f88d25d509d07881573b123ca22e5ae9902971d Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Tue, 20 Feb 2024 18:45:09 +0100 Subject: [PATCH 10/15] added skos tree configuration parameters for frontend in EBISPOT#625 and TIBHannover#1 --- .../ols/controller/api/v1/TopConceptEnum.java | 2 +- .../spot/ols/model/v1/V1OntologyConfig.java | 5 +++++ .../v1/mappers/V1OntologyMapper.java | 8 ++++++-- dataload/configs/skos_ontologies.json | 18 ++++++++++++++---- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/TopConceptEnum.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/TopConceptEnum.java index 95aceccbc..3a86e8c58 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/TopConceptEnum.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/TopConceptEnum.java @@ -3,5 +3,5 @@ public enum TopConceptEnum { SCHEMA, TOPCONCEPTOF_PROPERTY, - RELATIONSHIPS, + RELATIONSHIPS } diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/model/v1/V1OntologyConfig.java b/backend/src/main/java/uk/ac/ebi/spot/ols/model/v1/V1OntologyConfig.java index cbd2db376..79c6d23d7 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/model/v1/V1OntologyConfig.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/model/v1/V1OntologyConfig.java @@ -1,6 +1,7 @@ package uk.ac.ebi.spot.ols.model.v1; import com.google.gson.annotations.SerializedName; +import uk.ac.ebi.spot.ols.controller.api.v1.TopConceptEnum; import java.util.Collection; import java.util.HashSet; @@ -47,5 +48,9 @@ public class V1OntologyConfig { public Collection preferredRootTerms = new HashSet<>(); public boolean isSkos; + public boolean skosNarrower; + + public TopConceptEnum skosRoot; + public boolean allowDownload; } diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/mappers/V1OntologyMapper.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/mappers/V1OntologyMapper.java index 22f62e4db..0aa47ffab 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/mappers/V1OntologyMapper.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/mappers/V1OntologyMapper.java @@ -3,6 +3,7 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import uk.ac.ebi.spot.ols.controller.api.v1.TopConceptEnum; import uk.ac.ebi.spot.ols.model.v1.V1Ontology; import uk.ac.ebi.spot.ols.model.v1.V1OntologyConfig; import uk.ac.ebi.spot.ols.repository.transforms.LocalizationTransform; @@ -61,9 +62,12 @@ public static V1Ontology mapOntology(JsonElement json, String lang) { ontology.config.preferredRootTerms = JsonHelper.getStrings(localizedJson, "preferredRootTerms"); ontology.config.isSkos = localizedJson.has("isSkos") && localizedJson.get("isSkos").getAsBoolean(); + if(ontology.config.isSkos) { + ontology.config.skosNarrower = localizedJson.has("skosNarrower") && localizedJson.get("skosNarrower").getAsBoolean(); + if (localizedJson.has("skosRoot")) + ontology.config.skosRoot = TopConceptEnum.valueOf(localizedJson.get("skosRoot").getAsString()); + } ontology.config.allowDownload = localizedJson.has("allowDownload") && localizedJson.get("allowDownload").getAsBoolean(); - - ontology.status = "LOADED"; ontology.numberOfTerms = Integer.parseInt(JsonHelper.getString(localizedJson, "numberOfClasses")); diff --git a/dataload/configs/skos_ontologies.json b/dataload/configs/skos_ontologies.json index 6aa93a543..15f626247 100644 --- a/dataload/configs/skos_ontologies.json +++ b/dataload/configs/skos_ontologies.json @@ -32,8 +32,10 @@ "https://doi.org/10.29172" ], "repo_url": "https://github.com/physh-org/PhySH", - "isSkos": true -}, + "isSkos": true, + "skosNarrower": false, + "skosRoot": "RELATIONSHIPS" + }, { "ontology_purl": "https://raw.githubusercontent.com/astrothesaurus/UAT/master/UAT.rdf", "title": "Unified Astronomy Thesaurus (UAT)", @@ -62,8 +64,10 @@ "http://www.w3.org/2004/02/skos/core#definition" ], "repo_url": "https://github.com/astrothesaurus/UAT", - "isSkos": true -}, + "isSkos": true, + "skosNarrower": false, + "skosRoot": "TOPCONCEPTOF_PROPERTY" + }, { "ontology_purl": "https://raw.githubusercontent.com/dini-ag-kim/hochschulfaechersystematik/master/hochschulfaechersystematik.ttl", "id": "hsfs", @@ -78,6 +82,8 @@ ], "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", "isSkos": true, + "skosNarrower": false, + "skosRoot": "TOPCONCEPTOF_PROPERTY", "creator": [ "Michael Menzel", "Adrian Pohl" @@ -110,6 +116,8 @@ "https://purl.org/fidbaudigital/subjects" ], "isSkos": true, + "skosNarrower": false, + "skosRoot": "TOPCONCEPTOF_PROPERTY", "repo_url": "https://gitlab.com/fid-bau/terminologie/fid-baudigital-faecherklassifikation" }, { @@ -128,6 +136,8 @@ "allow_download": true, "preferredPrefix": "oefos", "isSkos": true, + "skosNarrower": false, + "skosRoot": "SCHEMA", "label_property": "http://www.w3.org/2004/02/skos/core#prefLabel", "creator": [ "Christoph Hoffmann" From e56e916b7cdb820be11a21f885a6859e343f5ad7 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:59:06 +0100 Subject: [PATCH 11/15] refactored and handled null cases --- .../ols/repository/v1/V1TermRepository.java | 175 +++++++++--------- 1 file changed, 87 insertions(+), 88 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java index 3b8e72246..6be9713db 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v1/V1TermRepository.java @@ -4,10 +4,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; import uk.ac.ebi.spot.ols.model.v1.V1Individual; @@ -311,41 +309,41 @@ public List> conceptTree (String ontologyId, boolean schema, bo listOfTerms.addAll(terms.getContent()); } - List> rootIndividuals = new ArrayList>(); + List> rootTerms = new ArrayList>(); int count = 0; if(schema) { - for (V1Term indiv : listOfTerms) - if (indiv.annotation.get("hasTopConcept") != null) { - for (String iriTopConcept : (LinkedHashSet) indiv.annotation.get("hasTopConcept")) { - V1Term topConceptIndividual = findIndividual(listOfTerms,iriTopConcept); - TreeNode topConcept = new TreeNode(topConceptIndividual); + for (V1Term term : listOfTerms) + if (term.annotation.get("hasTopConcept") != null) { + for (String iriTopConcept : (LinkedHashSet) term.annotation.get("hasTopConcept")) { + V1Term topConceptTerm = findTerm(listOfTerms,iriTopConcept); + TreeNode topConcept = new TreeNode(topConceptTerm); topConcept.setIndex(String.valueOf(++count)); if(withChildren) { if(narrower) - populateChildrenandRelatedByNarrower(topConceptIndividual,topConcept,listOfTerms); + populateChildrenandRelatedByNarrower(topConceptTerm,topConcept,listOfTerms); else - populateChildrenandRelatedByBroader(topConceptIndividual,topConcept,listOfTerms); + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); } - rootIndividuals.add(topConcept); + rootTerms.add(topConcept); } } - } else for (V1Term individual : listOfTerms) { - TreeNode tree = new TreeNode(individual); + } else for (V1Term term : listOfTerms) { + TreeNode tree = new TreeNode(term); - if (tree.isRoot() && individual.annotation.get("topConceptOf") != null) { + if (tree.isRoot() && term.annotation.get("topConceptOf") != null) { tree.setIndex(String.valueOf(++count)); if(withChildren) { if(narrower) - populateChildrenandRelatedByNarrower(individual,tree,listOfTerms); + populateChildrenandRelatedByNarrower(term,tree,listOfTerms); else - populateChildrenandRelatedByBroader(individual,tree,listOfTerms); + populateChildrenandRelatedByBroader(term,tree,listOfTerms); } - rootIndividuals.add(tree); + rootTerms.add(tree); } } - return rootIndividuals; + return rootTerms; } @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#narrower).concat('-').concat(#withChildren)") @@ -360,54 +358,55 @@ public List> conceptTreeWithoutTop (String ontologyId, boolean } Set rootIRIs = new HashSet(); - List> rootIndividuals = new ArrayList>(); + List> rootTerms = new ArrayList>(); int count = 0; if(!narrower) { - for (V1Term individual : listOfTerms) { - if (individual.annotation.get("broader") != null) { - for (String iriBroader : (LinkedHashSet) individual.annotation.get("broader")) { - V1Term broaderIndividual = findIndividual(listOfTerms,iriBroader); - if (broaderIndividual.annotation.get("broader") == null) { - rootIRIs.add(iriBroader); - } - } - } + for (V1Term term : listOfTerms) { + if(term.annotation != null && term.annotation.get("broader") != null) { + for (String iriBroader : (LinkedHashSet) term.annotation.get("broader")) { + V1Term broaderTerm = findTerm(listOfTerms, iriBroader); + if (broaderTerm.annotation != null && broaderTerm.annotation.get("broader") == null) { + rootIRIs.add(iriBroader); + } + + } + } } for (String iri : rootIRIs) { - V1Term topConceptIndividual = findIndividual(listOfTerms, iri); - TreeNode topConcept = new TreeNode(topConceptIndividual); + V1Term topConceptTerm = findTerm(listOfTerms, iri); + TreeNode topConcept = new TreeNode(topConceptTerm); topConcept.setIndex(String.valueOf(++count)); if(withChildren) - populateChildrenandRelatedByBroader(topConceptIndividual,topConcept,listOfTerms); - rootIndividuals.add(topConcept); + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); + rootTerms.add(topConcept); } } else { - for (V1Term individual : listOfTerms) { - if (individual.annotation.get("narrower") != null) { - boolean root = true; - for (V1Term indiv : listOfTerms) { - if (indiv.annotation.get("narrower") != null) { - for (String iriNarrower : (LinkedHashSet) indiv.annotation.get("narrower")) { - if (individual.iri.equals(iriNarrower)) - root = false; - } - } - } - - if(root) { - TreeNode topConcept = new TreeNode(individual); - topConcept.setIndex(String.valueOf(++count)); - if(withChildren) - populateChildrenandRelatedByNarrower(individual,topConcept,listOfTerms); - rootIndividuals.add(topConcept); - } - } + for (V1Term term : listOfTerms) { + if (term.annotation != null && term.annotation.get("narrower") != null) { + boolean root = true; + for (V1Term v1Term : listOfTerms) { + if (v1Term.annotation != null && v1Term.annotation.get("narrower") != null) { + for (String iriNarrower : (LinkedHashSet) v1Term.annotation.get("narrower")) { + if (term.iri.equals(iriNarrower)) + root = false; + } + } + } + + if (root) { + TreeNode topConcept = new TreeNode(term); + topConcept.setIndex(String.valueOf(++count)); + if (withChildren) + populateChildrenandRelatedByNarrower(term, topConcept, listOfTerms); + rootTerms.add(topConcept); + } + } } } - return rootIndividuals; + return rootTerms; } @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat('s').concat('-').concat(#iri).concat('-').concat(#narrower).concat('-').concat(#index)") @@ -421,30 +420,30 @@ public TreeNode conceptSubTree(String ontologyId, String iri, boolean na listOfTerms.addAll(terms.getContent()); } - V1Term topConceptIndividual = findIndividual(listOfTerms,iri); - TreeNode topConcept = new TreeNode(topConceptIndividual); + V1Term topConceptTerm = findTerm(listOfTerms,iri); + TreeNode topConcept = new TreeNode(topConceptTerm); topConcept.setIndex(index); if(narrower) - populateChildrenandRelatedByNarrower(topConceptIndividual,topConcept,listOfTerms); + populateChildrenandRelatedByNarrower(topConceptTerm,topConcept,listOfTerms); else - populateChildrenandRelatedByBroader(topConceptIndividual,topConcept,listOfTerms); + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); return topConcept; } - public V1Term findIndividual(List wholeList, String iri) { - for (V1Term individual : wholeList) - if(individual.iri.equals(iri)) - return individual; + public V1Term findTerm(List wholeList, String iri) { + for (V1Term term : wholeList) + if(term.iri.equals(iri)) + return term; return new V1Term(); } public List findRelated(String ontologyId, String iri, String relationType, String lang) { List related = new ArrayList(); - V1Term individual = this.findByOntologyAndIri(ontologyId, iri, lang); - if (individual != null) - if (individual.annotation.get(relationType) != null) - for (String iriBroader : (LinkedHashSet) individual.annotation.get(relationType)) + V1Term term = this.findByOntologyAndIri(ontologyId, iri, lang); + if (term != null) + if (term.annotation.get(relationType) != null) + for (String iriBroader : (LinkedHashSet) term.annotation.get(relationType)) related.add(this.findByOntologyAndIri(ontologyId, iriBroader, lang)); return related; @@ -453,10 +452,10 @@ public List findRelated(String ontologyId, String iri, String relationTy public ListfindRelatedIndirectly(String ontologyId, String iri, String relationType, Boolean obsoletes, String lang, Pageable pageable){ List related = new ArrayList(); - V1Term individual = this.findByOntologyAndIri(ontologyId, iri, lang); - if(individual == null) + V1Term v1Term = this.findByOntologyAndIri(ontologyId, iri, lang); + if(v1Term == null) return related; - if(individual.iri == null) + if(v1Term.iri == null) return related; Page terms = this.findAllByOntology(ontologyId, obsoletes, lang, pageable); @@ -479,41 +478,41 @@ public List findRelated(String ontologyId, String iri, String relationTy return related; } - public void populateChildrenandRelatedByNarrower(V1Term individual, TreeNode tree, List listOfTerms ) { + public void populateChildrenandRelatedByNarrower(V1Term term, TreeNode tree, List listOfTerms ) { - if (individual.annotation != null) - for (String iriRelated : (LinkedHashSet) individual.annotation.getOrDefault("related", new LinkedHashSet())) { - TreeNode related = new TreeNode(findIndividual(listOfTerms, iriRelated)); + if (term.annotation != null) + for (String iriRelated : (LinkedHashSet) term.annotation.getOrDefault("related", new LinkedHashSet())) { + TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); related.setIndex(tree.getIndex() + ".related"); tree.addRelated(related); } int count = 0; - if (individual.annotation != null) - for (String iriChild : (LinkedHashSet) individual.annotation.getOrDefault("narrower", new LinkedHashSet())) { - V1Term childIndividual = findIndividual(listOfTerms, iriChild); - TreeNode child = new TreeNode(childIndividual); + if (term.annotation != null) + for (String iriChild : (LinkedHashSet) term.annotation.getOrDefault("narrower", new LinkedHashSet())) { + V1Term childTerm = findTerm(listOfTerms, iriChild); + TreeNode child = new TreeNode(childTerm); child.setIndex(tree.getIndex() + "." + ++count); - populateChildrenandRelatedByNarrower(childIndividual, child, listOfTerms); + populateChildrenandRelatedByNarrower(childTerm, child, listOfTerms); tree.addChild(child); } } - public void populateChildrenandRelatedByBroader(V1Term individual, TreeNode tree, List listOfTerms) { - if (individual.annotation != null) - for (String iriRelated : (LinkedHashSet) individual.annotation.getOrDefault("related", new LinkedHashSet())) { - TreeNode related = new TreeNode(findIndividual(listOfTerms, iriRelated)); + public void populateChildrenandRelatedByBroader(V1Term term, TreeNode tree, List listOfTerms) { + if (term.annotation != null) + for (String iriRelated : (LinkedHashSet) term.annotation.getOrDefault("related", new LinkedHashSet())) { + TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); related.setIndex(tree.getIndex() + ".related"); tree.addRelated(related); } int count = 0; - for ( V1Term indiv : listOfTerms) { - if (indiv.annotation != null) - for (String iriBroader : (LinkedHashSet) indiv.annotation.getOrDefault("broader",new LinkedHashSet())) - if(individual.iri != null) - if (individual.iri.equals(iriBroader)) { - TreeNode child = new TreeNode(indiv); + for ( V1Term v1Term : listOfTerms) { + if (v1Term.annotation != null) + for (String iriBroader : (LinkedHashSet) v1Term.annotation.getOrDefault("broader",new LinkedHashSet())) + if(term.iri != null) + if (term.iri.equals(iriBroader)) { + TreeNode child = new TreeNode(v1Term); child.setIndex(tree.getIndex()+"."+ ++count); - populateChildrenandRelatedByBroader(indiv,child,listOfTerms); + populateChildrenandRelatedByBroader(v1Term,child,listOfTerms); tree.addChild(child); } } From 49b63a4d27bcf26144e9c92380966b6aa26431e2 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Wed, 3 Apr 2024 19:46:21 +0200 Subject: [PATCH 12/15] implemented skos v2 api for EBISPOT#625 and TIBHannover#1 --- .../controller/api/v2/V2ClassController.java | 2 +- .../v2/V2OntologySKOSConceptController.java | 395 ++++++++++++++++++ .../ols/repository/solr/OlsSolrClient.java | 4 +- .../ols/repository/v2/V2ClassRepository.java | 246 ++++++++++- 4 files changed, 640 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java index 3c1fd9e9a..b35203b4c 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2ClassController.java @@ -98,7 +98,7 @@ public HttpEntity getClass( iri = UriUtils.decode(iri, "UTF-8"); - V2Entity entity = classRepository.getByOntologyIdAndIri(ontologyId, iri, lang); + V2Entity entity = classRepository.findByOntologyAndIri(ontologyId, iri, lang); if (entity == null) throw new ResourceNotFoundException(); return new ResponseEntity<>( entity, HttpStatus.OK); } diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java new file mode 100644 index 000000000..55147f0c7 --- /dev/null +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java @@ -0,0 +1,395 @@ +package uk.ac.ebi.spot.ols.controller.api.v2; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.google.gson.JsonObject; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.data.web.PagedResourcesAssembler; +import org.springframework.hateoas.MediaTypes; +import org.springframework.hateoas.PagedModel; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.util.UriUtils; +import uk.ac.ebi.spot.ols.controller.api.v1.TopConceptEnum; +import uk.ac.ebi.spot.ols.controller.api.v1.V1TermAssembler; +import uk.ac.ebi.spot.ols.model.v1.V1Term; +import uk.ac.ebi.spot.ols.model.v2.V2Entity; +import uk.ac.ebi.spot.ols.repository.v1.TreeNode; +import uk.ac.ebi.spot.ols.repository.v1.V1TermRepository; +import uk.ac.ebi.spot.ols.repository.v2.V2ClassRepository; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.*; + +/** + * @author Simon Jupp + * @date 02/11/15 + * Samples, Phenotypes and Ontologies Team, EMBL-EBI + */ +@RestController +@RequestMapping("/api/v2/ontologies") +@Tag(name = "v2-ontology-skos-controller", description = "SKOS concept hierarchies and relations extracted from individuals (instances) from a particular ontology in this service") +public class V2OntologySKOSConceptController { + + @Autowired + V2ClassRepository classRepository; + + @Operation(description = "Get complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/conceptrelations/{iri} method with broader or narrower concept relations.") + @RequestMapping(path = "/{onto}/skos/tree", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + HttpEntity>> getSKOSConceptHierarchyByOntology( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "infer top concepts by schema (hasTopConcept) or TopConceptOf property or broader/narrower relationships", required = true) + @RequestParam(value = "find_roots", required = true, defaultValue = "SCHEMA") TopConceptEnum topConceptIdentification, + @Parameter(description = "infer from narrower or broader relationships", required = true) + @RequestParam(value = "narrower", required = true, defaultValue = "false") boolean narrower, + @Parameter(description = "Extract the whole tree with children or only the top concepts", required = true) + @RequestParam(value = "with_children", required = true, defaultValue = "false") boolean withChildren, + @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) throws IOException { + ontologyId = ontologyId.toLowerCase(); + if (TopConceptEnum.RELATIONSHIPS == topConceptIdentification) + return new ResponseEntity<>(classRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable), HttpStatus.OK); + else + return new ResponseEntity<>(classRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable), HttpStatus.OK); + } + + @Operation(description = "Display complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/displayconceptrelations/{iri} method with broader or narrower concept relations.") + @RequestMapping(path = "/{onto}/skos/displaytree", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @ResponseBody + HttpEntity displaySKOSConceptHierarchyByOntology( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "infer top concepts by schema (hasTopConcept) or TopConceptOf property or broader/narrower relationships", required = true) + @RequestParam(value = "find_roots", required = true, defaultValue = "SCHEMA") TopConceptEnum topConceptIdentification, + @Parameter(description = "infer from narrower or broader relationships", required = true) + @RequestParam(value = "narrower", required = true, defaultValue = "false") boolean narrower, + @Parameter(description = "Extract the whole tree with children or only the top concepts", required = true) + @RequestParam(value = "with_children", required = true, defaultValue = "false") boolean withChildren, + @Parameter(description = "display related concepts", required = true) + @RequestParam(value = "display_related", required = true, defaultValue = "false") boolean displayRelated, + @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) throws IOException { + ontologyId = ontologyId.toLowerCase(); + List> rootIndividuals = null; + if(TopConceptEnum.RELATIONSHIPS == topConceptIdentification) + rootIndividuals = classRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable); + else + rootIndividuals = classRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable); + StringBuilder sb = new StringBuilder(); + for (TreeNode root : rootIndividuals) { + sb.append(root.getIndex() + " , "+ root.getData().any().get("label").toString() + " , " + root.getData().any().get("iri").toString()).append("\n"); + sb.append(generateConceptHierarchyTextByOntology(root, displayRelated)); + } + + return new HttpEntity(sb.toString()); + } + + @Operation(description = "Get partial SKOS concept hierarchy based on the encoded iri of the designated top concept") + @RequestMapping(path = "/{onto}/skos/{iri}/tree", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + HttpEntity> getSKOSConceptHierarchyByOntologyAndIri( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "infer from narrower or broader relationships", required = true) + @RequestParam(value = "narrower", required = true, defaultValue = "false") boolean narrower, + @Parameter(description = "index value for the root term", required = true) + @RequestParam(value = "index", required = true, defaultValue = "1") String index, + @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) throws IOException { + ontologyId = ontologyId.toLowerCase(); + TreeNode topConcept = new TreeNode(new V2Entity(new JsonObject())); + String decodedIri; + decodedIri = UriUtils.decode(iri, "UTF-8"); + topConcept = classRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); + + if (topConcept.getData().any().get("iri").toString() == null) + throw new ResourceNotFoundException("No roots could be found for " + ontologyId ); + return new ResponseEntity<>(topConcept, HttpStatus.OK); + } + + @Operation(description = "Display partial SKOS concept hierarchy based on the encoded iri of the designated top concept") + @RequestMapping(path = "/{onto}/skos/{iri}/displaytree", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @ResponseBody + HttpEntity displaySKOSConceptHierarchyByOntologyAndIri( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "infer from narrower or broader relationships", required = true) + @RequestParam(value = "narrower", required = true, defaultValue = "false") boolean narrower, + @Parameter(description = "display related concepts", required = true) + @RequestParam(value = "display_related", required = true, defaultValue = "false") boolean displayRelated, + @Parameter(description = "index value for the root term", required = true) + @RequestParam(value = "index", required = true, defaultValue = "1") String index, + @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) throws IOException { + ontologyId = ontologyId.toLowerCase(); + TreeNode topConcept = new TreeNode(new V2Entity(new JsonObject())); + String decodedIri; + StringBuilder sb = new StringBuilder(); + decodedIri = UriUtils.decode(iri, "UTF-8"); + topConcept = classRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); + + sb.append(topConcept.getIndex() + " , "+ topConcept.getData().any().get("label").toString() + " , " + topConcept.getData().any().get("iri").toString()).append("\n"); + sb.append(generateConceptHierarchyTextByOntology(topConcept, displayRelated)); + + return new HttpEntity(sb.toString()); + } + + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format.") + @RequestMapping(path = "/{onto}/skos/{iri}/relations", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + public HttpEntity> findRelatedConcepts( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "skos based concept relation type", required = true) + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable, + PagedResourcesAssembler assembler) { + + ontologyId = ontologyId.toLowerCase(); + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + related = classRepository.findRelated(ontologyId, decodedIri, relationType,lang); + + + final int start = (int)pageable.getOffset(); + final int end = Math.min((start + pageable.getPageSize()), related.size()); + Page conceptPage = new PageImpl<>(related.subList(start, end), pageable, related.size()); + + return new ResponseEntity<>( assembler.toModel(conceptPage), HttpStatus.OK); + + } + + @Operation(description = "Broader, Narrower and Related concept relations of a concept are displayed as text if the concept iri is provided in encoded format.") + @RequestMapping(path = "/{onto}/skos/{iri}/displayrelations", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @ResponseBody + public HttpEntity displayRelatedConcepts( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "skos based concept relation type", required = true) + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable, + PagedResourcesAssembler assembler) { + StringBuilder sb = new StringBuilder(); + ontologyId = ontologyId.toLowerCase(); + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + related = classRepository.findRelated(ontologyId, decodedIri, relationType,lang); + + final int start = (int)pageable.getOffset(); + final int end = Math.min((start + pageable.getPageSize()), related.size()); + Page conceptPage = new PageImpl<>(related.subList(start, end), pageable, related.size()); + int count = 0; + for (V2Entity individual : conceptPage.getContent()) + sb.append(++count).append(" , ").append(individual.any().get("label").toString()).append(" , ").append(individual.any().get("iri").toString()).append("\n"); + + return new HttpEntity<>( sb.toString()); + + } + + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format. The relationship is identified indirectly based on the related concept's relation to the concept in question. This requires traversing all the available concepts and checking if they are related to the concept in question. For this reason, this method is relatively slower than the displayconceptrelations method. Nevertheless, it enables to identify unforeseen relations of the concept in question") + @RequestMapping(path = "/{onto}/skos/{iri}/indirectrelations", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + public HttpEntity> findRelatedConceptsIndirectly( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "skos based concept relation type", required = true) + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) throws IOException { + + ontologyId = ontologyId.toLowerCase(); + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + related = classRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType, obsoletes,lang,pageable); + + return new ResponseEntity<>( related, HttpStatus.OK); + + } + + @Operation(description = "Broader, Narrower and Related concept relations of a concept are listed in JSON if the concept iri is provided in encoded format. The relationship is identified indirectly based on the related concept's relation to the concept in question. This requires traversing all the available concepts and checking if they are related to the concept in question. For this reason, this method is relatively slower than the displayconceptrelations method. Nevertheless, it enables to identify unforeseen relations of the concept in question") + @RequestMapping(path = "/{onto}/skos/{iri}/displayindirectrelations", produces = {MediaType.TEXT_PLAIN_VALUE}, method = RequestMethod.GET) + @ResponseBody + public HttpEntity displayRelatedConceptsIndirectly( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @Parameter(description = "skos based concept relation type", required = true) + @RequestParam(value = "relation_type", required = true, defaultValue = "broader") + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @Parameter(description = "Page size to retrieve individuals", required = true) + @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, + Pageable pageable) throws IOException { + StringBuilder sb = new StringBuilder(); + ontologyId = ontologyId.toLowerCase(); + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + related = classRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType, obsoletes,lang,pageable); + + int count = 0; + for (V2Entity individual : related) + sb.append(++count).append(" , ").append(individual.any().get("label").toString()).append(" , ").append(individual.any().get("iri").toString()).append("\n"); + + + return new ResponseEntity<>( sb.toString(), HttpStatus.OK); + + } + + @Operation(description = "Node and Edge definitions needed to visualize the nodes that are directly related with the subject term. Ontology ID and encoded iri are required. ") + @RequestMapping(path = "/{onto}/skos/{iri}/graph", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) + public HttpEntity retrieveImmediateGraph( + @Parameter(description = "ontology ID", required = true) + @PathVariable("onto") String ontologyId, + @Parameter(description = "encoded concept IRI", required = true) + @PathVariable("iri") String iri, + @RequestParam(value = "lang", required = false, defaultValue = "en") String lang){ + + List related = new ArrayList(); + String decodedIri = UriUtils.decode(iri, "UTF-8"); + + V2Entity subjectTerm = classRepository.findByOntologyAndIri(ontologyId, decodedIri, lang); + + related = classRepository.findRelated(ontologyId, decodedIri, "related",lang); + + List narrower = new ArrayList(); + narrower = classRepository.findRelated(ontologyId, decodedIri, "narrower",lang); + + List broader = new ArrayList(); + broader = classRepository.findRelated(ontologyId, decodedIri, "broader",lang); + + Set relatedNodes = new HashSet(); + related.forEach(term -> relatedNodes.add(new Node(term.any().get("iri").toString(), term.any().get("label").toString()))); + Set narrowerNodes = new HashSet(); + narrower.forEach(term -> narrowerNodes.add(new Node(term.any().get("iri").toString(), term.any().get("label").toString()))); + Set broaderNodes = new HashSet(); + broader.forEach(term -> broaderNodes.add(new Node(term.any().get("iri").toString(), term.any().get("label").toString()))); + + Set edges = new HashSet(); + relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "related","http://www.w3.org/2004/02/skos/core#related"))); + narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "narrower","http://www.w3.org/2004/02/skos/core#narrower"))); + broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "broader","http://www.w3.org/2004/02/skos/core#broader"))); + + Set nodes = new HashSet(); + nodes.add(new Node(decodedIri,subjectTerm.any().get("label").toString())); + nodes.addAll(relatedNodes); + nodes.addAll(broaderNodes); + nodes.addAll(narrowerNodes); + + + Map graph = new HashMap(); + graph.put("nodes", nodes); + graph.put("edges", edges); + ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + try { + return new ResponseEntity<>(ow.writeValueAsString(graph),HttpStatus.OK); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public StringBuilder generateConceptHierarchyTextByOntology(TreeNode rootConcept, boolean displayRelated) { + StringBuilder sb = new StringBuilder(); + for (TreeNode childConcept : rootConcept.getChildren()) { + sb.append(childConcept.getIndex() + " , "+ childConcept.getData().any().get("label").toString() + " , " + childConcept.getData().any().get("iri").toString()).append("\n"); + sb.append(generateConceptHierarchyTextByOntology(childConcept,displayRelated)); + } + if(displayRelated) + for (TreeNode relatedConcept : rootConcept.getRelated()) { + sb.append(relatedConcept.getIndex() + " , "+ relatedConcept.getData().any().get("label").toString() + " , " + relatedConcept.getData().any().get("iri").toString()).append("\n"); + sb.append(generateConceptHierarchyTextByOntology(relatedConcept,displayRelated)); + } + return sb; + } + + @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found") + @ExceptionHandler(ResourceNotFoundException.class) + public void handleError(HttpServletRequest req, Exception exception) { + } + + public class Node { + String iri; + String label; + + public Node(String iri, String label) { + this.iri = iri; + this.label = label; + } + + public String getIri() { + return iri; + } + + public String getLabel() { + return label; + } + + } + + public class Edge { + String source; + String target; + String label; + String uri; + + public Edge(String source, String target, String label, String uri) { + this.source = source; + this.target = target; + this.label = label; + this.uri = uri; + } + + public String getSource() { + return source; + } + + public String getTarget() { + return target; + } + + public String getLabel() { + return label; + } + + public String getUri() { + return uri; + } + + } + +} diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java index a1cb659e4..c2cd47052 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -95,7 +96,8 @@ public JsonElement getFirst(OlsSolrQuery query) { if(qr.getResults().getNumFound() < 1) { logger.debug("Expected at least 1 result for solr getFirst for solr query = {}", query.constructQuery().jsonStr()); - throw new RuntimeException("Expected at least 1 result for solr getFirst"); + return new JsonObject(); + //throw new RuntimeException("Expected at least 1 result for solr getFirst"); } return getOlsEntityFromSolrResult(qr.getResults().get(0)); diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2ClassRepository.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2ClassRepository.java index 9e621e734..2c0612941 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2ClassRepository.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2ClassRepository.java @@ -1,11 +1,15 @@ package uk.ac.ebi.spot.ols.repository.v2; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.stereotype.Component; +import uk.ac.ebi.spot.ols.controller.api.v2.helpers.DynamicQueryHelper; import uk.ac.ebi.spot.ols.model.v2.V2Entity; import uk.ac.ebi.spot.ols.repository.neo4j.OlsNeo4jClient; import uk.ac.ebi.spot.ols.repository.solr.SearchType; @@ -15,14 +19,12 @@ import uk.ac.ebi.spot.ols.repository.Validation; import uk.ac.ebi.spot.ols.repository.transforms.LocalizationTransform; import uk.ac.ebi.spot.ols.repository.transforms.RemoveLiteralDatatypesTransform; +import uk.ac.ebi.spot.ols.repository.v1.TreeNode; import uk.ac.ebi.spot.ols.repository.v2.helpers.V2DynamicFilterParser; import uk.ac.ebi.spot.ols.repository.v2.helpers.V2SearchFieldsParser; import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; @Component public class V2ClassRepository { @@ -33,6 +35,17 @@ public class V2ClassRepository { @Autowired OlsNeo4jClient neo4jClient; + public String broader = "http://www.w3.org/2004/02/skos/core#broader"; + + public String narrower = "http://www.w3.org/2004/02/skos/core#narrower"; + + public String related = "http://www.w3.org/2004/02/skos/core#related"; + + public String hasTopConcept = "http://www.w3.org/2004/02/skos/core#hasTopConcept"; + + public String topConceptOf = "http://www.w3.org/2004/02/skos/core#topConceptOf"; + + public OlsFacetedResultsPage find( Pageable pageable, String lang, String search, String searchFields, String boostFields, boolean exactMatch, Map> properties) throws IOException { @@ -82,7 +95,7 @@ public OlsFacetedResultsPage findByOntologyId( .map(V2Entity::new); } - public V2Entity getByOntologyIdAndIri(String ontologyId, String iri, String lang) throws ResourceNotFoundException { + public V2Entity findByOntologyAndIri(String ontologyId, String iri, String lang) throws ResourceNotFoundException { Validation.validateOntologyId(ontologyId); Validation.validateLang(lang); @@ -169,4 +182,227 @@ public Page getIndividualAncestorsByOntologyId(String ontologyId, Page .map(RemoveLiteralDatatypesTransform::transform) .map(V2Entity::new); } + + + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#schema).concat('-').concat(#narrower).concat('-').concat(#withChildren)") + public List> conceptTree (String ontologyId, boolean schema, boolean narrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable) throws IOException { + + Map> properties = new HashMap<>(); + if(!obsoletes) + properties.put("isObsolete", List.of("false")); + + List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); + + List> rootTerms = new ArrayList>(); + int count = 0; + + if(schema) { + for (V2Entity term : listOfTerms) + if (term.any().get(hasTopConcept) != null) { + for (String iriTopConcept : (ArrayList) term.any().get(hasTopConcept)) { + V2Entity topConceptTerm = findTerm(listOfTerms,iriTopConcept); + TreeNode topConcept = new TreeNode(topConceptTerm); + topConcept.setIndex(String.valueOf(++count)); + if(withChildren) { + if(narrower) + populateChildrenandRelatedByNarrower(topConceptTerm,topConcept,listOfTerms); + else + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); + } + rootTerms.add(topConcept); + } + } + } else for (V2Entity term : listOfTerms) { + TreeNode tree = new TreeNode(term); + + if (tree.isRoot() && term.any().get(topConceptOf) != null) { + tree.setIndex(String.valueOf(++count)); + if(withChildren) { + if(narrower) + populateChildrenandRelatedByNarrower(term,tree,listOfTerms); + else + populateChildrenandRelatedByBroader(term,tree,listOfTerms); + } + rootTerms.add(tree); + } + } + + return rootTerms; + } + + public List allClassesOfOntology(String ontologyId, Boolean obsoletes, Pageable pageable, String lang) throws IOException { + Map> properties = new HashMap<>(); + if(!obsoletes) + properties.put("isObsolete", List.of("false")); + + Page terms = this.findByOntologyId(ontologyId, pageable, lang, null, null, null, false, DynamicQueryHelper.filterProperties(properties)); + List listOfTerms = new ArrayList(); + listOfTerms.addAll(terms.getContent()); + + while(terms.hasNext()) { + terms = findByOntologyId(ontologyId, terms.nextPageable(), lang, null, null, null, false, DynamicQueryHelper.filterProperties(properties)); + listOfTerms.addAll(terms.getContent()); + } + + return listOfTerms; + } + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#narrower).concat('-').concat(#withChildren)") + public List> conceptTreeWithoutTop (String ontologyId, boolean isNarrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable) throws IOException { + + List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); + + Set rootIRIs = new HashSet(); + List> rootTerms = new ArrayList>(); + int count = 0; + if(!isNarrower) { + for (V2Entity term : listOfTerms) { + if(term.any() != null && term.any().get(broader) != null) { + for (String iriBroader : getRelationsAsList(term,broader)) { + V2Entity broaderTerm = findTerm(listOfTerms, iriBroader); + if (broaderTerm.any() != null && broaderTerm.any().get(broader) == null) { + rootIRIs.add(iriBroader); + } + + } + } + } + + for (String iri : rootIRIs) { + V2Entity topConceptTerm = findTerm(listOfTerms, iri); + TreeNode topConcept = new TreeNode(topConceptTerm); + topConcept.setIndex(String.valueOf(++count)); + if(withChildren) + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); + rootTerms.add(topConcept); + } + + } else { + for (V2Entity term : listOfTerms) { + if (term.any() != null && term.any().get(narrower) != null) { + boolean root = true; + for (V2Entity V2Entity : listOfTerms) { + if (V2Entity.any() != null && V2Entity.any().get(narrower) != null) { + for (String iriNarrower : getRelationsAsList(V2Entity,narrower)) { + if (term.any().get("iri").equals(iriNarrower)) + root = false; + } + } + } + + if (root) { + TreeNode topConcept = new TreeNode(term); + topConcept.setIndex(String.valueOf(++count)); + if (withChildren) + populateChildrenandRelatedByNarrower(term, topConcept, listOfTerms); + rootTerms.add(topConcept); + } + } + } + } + + return rootTerms; + } + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat('s').concat('-').concat(#iri).concat('-').concat(#narrower).concat('-').concat(#index)") + public TreeNode conceptSubTree(String ontologyId, String iri, boolean narrower, String index, Boolean obsoletes, String lang, Pageable pageable) throws IOException { + List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); + V2Entity topConceptTerm = findTerm(listOfTerms,iri); + TreeNode topConcept = new TreeNode(topConceptTerm); + topConcept.setIndex(index); + if(narrower) + populateChildrenandRelatedByNarrower(topConceptTerm,topConcept,listOfTerms); + else + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); + + return topConcept; + } + + public V2Entity findTerm(List wholeList, String iri) { + for (V2Entity term : wholeList) + if(term.any().get("iri").equals(iri)) + return term; + return new V2Entity(new JsonObject()); + } + + public List findRelated(String ontologyId, String iri, String relationType, String lang) { + List related = new ArrayList(); + V2Entity term = this.findByOntologyAndIri(ontologyId, iri, lang); + if (term != null) + if (term.any().get(relationType) != null) + for (String iriBroader : getRelationsAsList(term,relationType)) + related.add(this.findByOntologyAndIri(ontologyId, iriBroader, lang)); + + return related; + } + + public ListfindRelatedIndirectly(String ontologyId, String iri, String relationType, Boolean obsoletes, String lang, Pageable pageable) throws IOException { + List related = new ArrayList(); + + V2Entity V2Entity = this.findByOntologyAndIri(ontologyId, iri, lang); + if(V2Entity == null) + return related; + if(V2Entity.any().get("iri") == null) + return related; + + List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); + + for (V2Entity term : listOfTerms) { + if (term != null) + if (term.any().get(relationType) != null) + for (String iriRelated : getRelationsAsList(term,relationType)) + if(iriRelated.equals(iri)) + related.add(term); + } + + return related; + } + + public void populateChildrenandRelatedByNarrower(V2Entity term, TreeNode tree, List listOfTerms ) { + + if (term.any() != null) + for (String iriRelated : getRelationsAsList(term,related)) { + TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); + related.setIndex(tree.getIndex() + ".related"); + tree.addRelated(related); + } + int count = 0; + if (term.any() != null) + for (String iriChild : getRelationsAsList(term,narrower)) { + V2Entity childTerm = findTerm(listOfTerms, iriChild); + TreeNode child = new TreeNode(childTerm); + child.setIndex(tree.getIndex() + "." + ++count); + populateChildrenandRelatedByNarrower(childTerm, child, listOfTerms); + tree.addChild(child); + } + } + + public void populateChildrenandRelatedByBroader(V2Entity term, TreeNode tree, List listOfTerms) { + if (term.any() != null) + for (String iriRelated : getRelationsAsList(term,related)) { + TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); + related.setIndex(tree.getIndex() + ".related"); + tree.addRelated(related); + } + int count = 0; + for ( V2Entity V2Entity : listOfTerms) { + if (V2Entity.any() != null) + for (String iriBroader : getRelationsAsList(V2Entity,broader)) + if(term.any().get("iri") != null) + if (term.any().get("iri").equals(iriBroader)) { + TreeNode child = new TreeNode(V2Entity); + child.setIndex(tree.getIndex()+"."+ ++count); + populateChildrenandRelatedByBroader(V2Entity,child,listOfTerms); + tree.addChild(child); + } + } + } + + public List getRelationsAsList(V2Entity entity, String relationType){ + if(entity.any().get(relationType) instanceof String) + return Arrays.asList((String) entity.any().get(relationType)); + else + return (ArrayList) entity.any().getOrDefault(relationType, new ArrayList()); + } } From e4205e75ae51f52f7a388b8380318eaad18a0563 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Wed, 10 Apr 2024 15:58:44 +0200 Subject: [PATCH 13/15] refactored with enum for skos relations --- .../v2/V2OntologySKOSConceptController.java | 31 ++++++++-------- .../ebi/spot/ols/model/v2/SKOSRelation.java | 37 +++++++++++++++++++ .../ols/repository/v2/V2ClassRepository.java | 35 +++++++----------- 3 files changed, 65 insertions(+), 38 deletions(-) create mode 100644 backend/src/main/java/uk/ac/ebi/spot/ols/model/v2/SKOSRelation.java diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java index 55147f0c7..b61a38f17 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java @@ -27,6 +27,7 @@ import uk.ac.ebi.spot.ols.controller.api.v1.TopConceptEnum; import uk.ac.ebi.spot.ols.controller.api.v1.V1TermAssembler; import uk.ac.ebi.spot.ols.model.v1.V1Term; +import uk.ac.ebi.spot.ols.model.v2.SKOSRelation; import uk.ac.ebi.spot.ols.model.v2.V2Entity; import uk.ac.ebi.spot.ols.repository.v1.TreeNode; import uk.ac.ebi.spot.ols.repository.v1.V1TermRepository; @@ -166,7 +167,7 @@ public HttpEntity> findRelatedConcepts( @PathVariable("iri") String iri, @Parameter(description = "skos based concept relation type", required = true) @RequestParam(value = "relation_type", required = true, defaultValue = "broader") - @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) SKOSRelation relationType, @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, Pageable pageable, @@ -175,8 +176,7 @@ public HttpEntity> findRelatedConcepts( ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - related = classRepository.findRelated(ontologyId, decodedIri, relationType,lang); - + related = classRepository.findRelated(ontologyId, decodedIri, relationType.getPropertyName(),lang); final int start = (int)pageable.getOffset(); final int end = Math.min((start + pageable.getPageSize()), related.size()); @@ -196,7 +196,7 @@ public HttpEntity displayRelatedConcepts( @PathVariable("iri") String iri, @Parameter(description = "skos based concept relation type", required = true) @RequestParam(value = "relation_type", required = true, defaultValue = "broader") - @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) SKOSRelation relationType, @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, Pageable pageable, @@ -205,7 +205,7 @@ public HttpEntity displayRelatedConcepts( ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - related = classRepository.findRelated(ontologyId, decodedIri, relationType,lang); + related = classRepository.findRelated(ontologyId, decodedIri, relationType.getPropertyName(),lang); final int start = (int)pageable.getOffset(); final int end = Math.min((start + pageable.getPageSize()), related.size()); @@ -227,15 +227,14 @@ public HttpEntity> findRelatedConceptsIndirectly( @PathVariable("iri") String iri, @Parameter(description = "skos based concept relation type", required = true) @RequestParam(value = "relation_type", required = true, defaultValue = "broader") - @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) SKOSRelation relationType, @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, Pageable pageable) throws IOException { - ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - related = classRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType, obsoletes,lang,pageable); + related = classRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType.getPropertyName(), obsoletes,lang,pageable); return new ResponseEntity<>( related, HttpStatus.OK); @@ -251,7 +250,7 @@ public HttpEntity displayRelatedConceptsIndirectly( @PathVariable("iri") String iri, @Parameter(description = "skos based concept relation type", required = true) @RequestParam(value = "relation_type", required = true, defaultValue = "broader") - @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) String relationType, + @Schema(type = "string", allowableValues = { "broader", "narrower", "related" }) SKOSRelation relationType, @Parameter(description = "Page size to retrieve individuals", required = true) @RequestParam(value = "obsoletes", required = false, defaultValue = "false") Boolean obsoletes, @RequestParam(value = "lang", required = false, defaultValue = "en") String lang, @@ -260,7 +259,7 @@ public HttpEntity displayRelatedConceptsIndirectly( ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - related = classRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType, obsoletes,lang,pageable); + related = classRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType.getPropertyName(), obsoletes,lang,pageable); int count = 0; for (V2Entity individual : related) @@ -285,13 +284,13 @@ public HttpEntity retrieveImmediateGraph( V2Entity subjectTerm = classRepository.findByOntologyAndIri(ontologyId, decodedIri, lang); - related = classRepository.findRelated(ontologyId, decodedIri, "related",lang); + related = classRepository.findRelated(ontologyId, decodedIri, SKOSRelation.related.getPropertyName(), lang); List narrower = new ArrayList(); - narrower = classRepository.findRelated(ontologyId, decodedIri, "narrower",lang); + narrower = classRepository.findRelated(ontologyId, decodedIri, SKOSRelation.narrower.getPropertyName(), lang); List broader = new ArrayList(); - broader = classRepository.findRelated(ontologyId, decodedIri, "broader",lang); + broader = classRepository.findRelated(ontologyId, decodedIri, SKOSRelation.broader.getPropertyName(), lang); Set relatedNodes = new HashSet(); related.forEach(term -> relatedNodes.add(new Node(term.any().get("iri").toString(), term.any().get("label").toString()))); @@ -301,9 +300,9 @@ public HttpEntity retrieveImmediateGraph( broader.forEach(term -> broaderNodes.add(new Node(term.any().get("iri").toString(), term.any().get("label").toString()))); Set edges = new HashSet(); - relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "related","http://www.w3.org/2004/02/skos/core#related"))); - narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "narrower","http://www.w3.org/2004/02/skos/core#narrower"))); - broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "broader","http://www.w3.org/2004/02/skos/core#broader"))); + relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "related",SKOSRelation.related.getPropertyName()))); + narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "narrower",SKOSRelation.narrower.getPropertyName()))); + broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "broader",SKOSRelation.broader.getPropertyName()))); Set nodes = new HashSet(); nodes.add(new Node(decodedIri,subjectTerm.any().get("label").toString())); diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/model/v2/SKOSRelation.java b/backend/src/main/java/uk/ac/ebi/spot/ols/model/v2/SKOSRelation.java new file mode 100644 index 000000000..4450a8c62 --- /dev/null +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/model/v2/SKOSRelation.java @@ -0,0 +1,37 @@ +package uk.ac.ebi.spot.ols.model.v2; + +/** + * @author Erhun Giray TUNCAY + * @email giray.tuncay@tib.eu + * TIB-Leibniz Information Center for Science and Technology + */ +public enum SKOSRelation { + + broader("http://www.w3.org/2004/02/skos/core#broader"), + + narrower("http://www.w3.org/2004/02/skos/core#narrower"), + + related("http://www.w3.org/2004/02/skos/core#related"), + + hasTopConcept("http://www.w3.org/2004/02/skos/core#hasTopConcept"), + + topConceptOf("http://www.w3.org/2004/02/skos/core#topConceptOf"); + + private final String propertyName; + + SKOSRelation(String propertyName) { + this.propertyName = propertyName; + } + + public static String[] getNames() { + String[] commands = new String[SKOSRelation.values().length]; + for (int i = 0;i find( Pageable pageable, String lang, String search, String searchFields, String boostFields, boolean exactMatch, Map> properties) throws IOException { @@ -199,8 +191,8 @@ public List> conceptTree (String ontologyId, boolean schema, if(schema) { for (V2Entity term : listOfTerms) - if (term.any().get(hasTopConcept) != null) { - for (String iriTopConcept : (ArrayList) term.any().get(hasTopConcept)) { + if (term.any().get(hasTopConcept.getPropertyName()) != null) { + for (String iriTopConcept : (ArrayList) term.any().get(hasTopConcept.getPropertyName())) { V2Entity topConceptTerm = findTerm(listOfTerms,iriTopConcept); TreeNode topConcept = new TreeNode(topConceptTerm); topConcept.setIndex(String.valueOf(++count)); @@ -216,7 +208,7 @@ public List> conceptTree (String ontologyId, boolean schema, } else for (V2Entity term : listOfTerms) { TreeNode tree = new TreeNode(term); - if (tree.isRoot() && term.any().get(topConceptOf) != null) { + if (tree.isRoot() && term.any().get(topConceptOf.getPropertyName()) != null) { tree.setIndex(String.valueOf(++count)); if(withChildren) { if(narrower) @@ -258,10 +250,10 @@ public List> conceptTreeWithoutTop (String ontologyId, boolea int count = 0; if(!isNarrower) { for (V2Entity term : listOfTerms) { - if(term.any() != null && term.any().get(broader) != null) { - for (String iriBroader : getRelationsAsList(term,broader)) { + if(term.any() != null && term.any().get(broader.getPropertyName()) != null) { + for (String iriBroader : getRelationsAsList(term,broader.getPropertyName())) { V2Entity broaderTerm = findTerm(listOfTerms, iriBroader); - if (broaderTerm.any() != null && broaderTerm.any().get(broader) == null) { + if (broaderTerm.any() != null && broaderTerm.any().get(broader.getPropertyName()) == null) { rootIRIs.add(iriBroader); } @@ -284,7 +276,7 @@ public List> conceptTreeWithoutTop (String ontologyId, boolea boolean root = true; for (V2Entity V2Entity : listOfTerms) { if (V2Entity.any() != null && V2Entity.any().get(narrower) != null) { - for (String iriNarrower : getRelationsAsList(V2Entity,narrower)) { + for (String iriNarrower : getRelationsAsList(V2Entity,narrower.getPropertyName())) { if (term.any().get("iri").equals(iriNarrower)) root = false; } @@ -333,7 +325,6 @@ public List findRelated(String ontologyId, String iri, String relation if (term.any().get(relationType) != null) for (String iriBroader : getRelationsAsList(term,relationType)) related.add(this.findByOntologyAndIri(ontologyId, iriBroader, lang)); - return related; } @@ -362,14 +353,14 @@ public List findRelated(String ontologyId, String iri, String relation public void populateChildrenandRelatedByNarrower(V2Entity term, TreeNode tree, List listOfTerms ) { if (term.any() != null) - for (String iriRelated : getRelationsAsList(term,related)) { + for (String iriRelated : getRelationsAsList(term,related.getPropertyName())) { TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); related.setIndex(tree.getIndex() + ".related"); tree.addRelated(related); } int count = 0; if (term.any() != null) - for (String iriChild : getRelationsAsList(term,narrower)) { + for (String iriChild : getRelationsAsList(term,narrower.getPropertyName())) { V2Entity childTerm = findTerm(listOfTerms, iriChild); TreeNode child = new TreeNode(childTerm); child.setIndex(tree.getIndex() + "." + ++count); @@ -380,7 +371,7 @@ public void populateChildrenandRelatedByNarrower(V2Entity term, TreeNode tree, List listOfTerms) { if (term.any() != null) - for (String iriRelated : getRelationsAsList(term,related)) { + for (String iriRelated : getRelationsAsList(term,related.getPropertyName())) { TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); related.setIndex(tree.getIndex() + ".related"); tree.addRelated(related); @@ -388,7 +379,7 @@ public void populateChildrenandRelatedByBroader(V2Entity term, TreeNode child = new TreeNode(V2Entity); From 60eb8860564714f9416431831d99e17ee3709791 Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:36:18 +0200 Subject: [PATCH 14/15] refactored the whole skos functionaliuty of v1 and v2 for EBISPOT#625 and TIBHAnnover#1 --- .../v1/V1OntologySKOSConceptController.java | 66 +----- .../v2/V2OntologySKOSConceptController.java | 103 ++------- .../java/uk/ac/ebi/spot/ols/model/Edge.java | 37 +++ .../java/uk/ac/ebi/spot/ols/model/Node.java | 25 ++ .../spot/ols/model/{v2 => }/SKOSRelation.java | 2 +- .../ols/repository/v2/V2ClassRepository.java | 206 +---------------- .../ols/repository/v2/V2SKOSRepository.java | 217 ++++++++++++++++++ 7 files changed, 316 insertions(+), 340 deletions(-) create mode 100644 backend/src/main/java/uk/ac/ebi/spot/ols/model/Edge.java create mode 100644 backend/src/main/java/uk/ac/ebi/spot/ols/model/Node.java rename backend/src/main/java/uk/ac/ebi/spot/ols/model/{v2 => }/SKOSRelation.java (96%) create mode 100644 backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2SKOSRepository.java diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java index 44415e81c..42a8a94d9 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v1/V1OntologySKOSConceptController.java @@ -25,6 +25,9 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.util.UriUtils; +import uk.ac.ebi.spot.ols.model.Edge; +import uk.ac.ebi.spot.ols.model.Node; +import uk.ac.ebi.spot.ols.model.SKOSRelation; import uk.ac.ebi.spot.ols.model.v1.V1Term; import uk.ac.ebi.spot.ols.repository.v1.TreeNode; import uk.ac.ebi.spot.ols.repository.v1.V1TermRepository; @@ -33,9 +36,9 @@ import java.util.*; /** - * @author Simon Jupp - * @date 02/11/15 - * Samples, Phenotypes and Ontologies Team, EMBL-EBI + * @author Erhun Giray TUNCAY + * @email giray.tuncay@tib.eu + * TIB-Leibniz Information Center for Science and Technology */ @RestController @RequestMapping("/api/ontologies") @@ -302,9 +305,9 @@ public HttpEntity retrieveImmediateGraph( broader.forEach(term -> broaderNodes.add(new Node(term.iri, term.label))); Set edges = new HashSet(); - relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "related","http://www.w3.org/2004/02/skos/core#related"))); - narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "narrower","http://www.w3.org/2004/02/skos/core#narrower"))); - broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "broader","http://www.w3.org/2004/02/skos/core#broader"))); + relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.getIri(), "related", SKOSRelation.related.getPropertyName()))); + narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.getIri(), "narrower",SKOSRelation.narrower.getPropertyName()))); + broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.getIri(), "broader",SKOSRelation.broader.getPropertyName()))); Set nodes = new HashSet(); nodes.add(new Node(decodedIri,subjectTerm.label)); @@ -342,55 +345,4 @@ public StringBuilder generateConceptHierarchyTextByOntology(TreeNode roo @ExceptionHandler(ResourceNotFoundException.class) public void handleError(HttpServletRequest req, Exception exception) { } - - public class Node { - String iri; - String label; - - public Node(String iri, String label) { - this.iri = iri; - this.label = label; - } - - public String getIri() { - return iri; - } - - public String getLabel() { - return label; - } - - } - - public class Edge { - String source; - String target; - String label; - String uri; - - public Edge(String source, String target, String label, String uri) { - this.source = source; - this.target = target; - this.label = label; - this.uri = uri; - } - - public String getSource() { - return source; - } - - public String getTarget() { - return target; - } - - public String getLabel() { - return label; - } - - public String getUri() { - return uri; - } - - } - } diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java index b61a38f17..a8d102245 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/controller/api/v2/V2OntologySKOSConceptController.java @@ -8,8 +8,6 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -25,22 +23,21 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.util.UriUtils; import uk.ac.ebi.spot.ols.controller.api.v1.TopConceptEnum; -import uk.ac.ebi.spot.ols.controller.api.v1.V1TermAssembler; -import uk.ac.ebi.spot.ols.model.v1.V1Term; -import uk.ac.ebi.spot.ols.model.v2.SKOSRelation; +import uk.ac.ebi.spot.ols.model.Edge; +import uk.ac.ebi.spot.ols.model.Node; +import uk.ac.ebi.spot.ols.model.SKOSRelation; import uk.ac.ebi.spot.ols.model.v2.V2Entity; import uk.ac.ebi.spot.ols.repository.v1.TreeNode; -import uk.ac.ebi.spot.ols.repository.v1.V1TermRepository; -import uk.ac.ebi.spot.ols.repository.v2.V2ClassRepository; +import uk.ac.ebi.spot.ols.repository.v2.V2SKOSRepository; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.*; /** - * @author Simon Jupp - * @date 02/11/15 - * Samples, Phenotypes and Ontologies Team, EMBL-EBI + * @author Erhun Giray TUNCAY + * @email giray.tuncay@tib.eu + * TIB-Leibniz Information Center for Science and Technology */ @RestController @RequestMapping("/api/v2/ontologies") @@ -48,7 +45,7 @@ public class V2OntologySKOSConceptController { @Autowired - V2ClassRepository classRepository; + V2SKOSRepository skosRepository; @Operation(description = "Get complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/conceptrelations/{iri} method with broader or narrower concept relations.") @RequestMapping(path = "/{onto}/skos/tree", produces = {MediaType.APPLICATION_JSON_VALUE, MediaTypes.HAL_JSON_VALUE}, method = RequestMethod.GET) @@ -66,9 +63,9 @@ HttpEntity>> getSKOSConceptHierarchyByOntology( Pageable pageable) throws IOException { ontologyId = ontologyId.toLowerCase(); if (TopConceptEnum.RELATIONSHIPS == topConceptIdentification) - return new ResponseEntity<>(classRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable), HttpStatus.OK); + return new ResponseEntity<>(skosRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable), HttpStatus.OK); else - return new ResponseEntity<>(classRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable), HttpStatus.OK); + return new ResponseEntity<>(skosRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable), HttpStatus.OK); } @Operation(description = "Display complete SKOS concept hierarchy or only top concepts based on alternative top concept identification methods and concept relations. If only top concepts are identified, they can be used to extract the following levels of the concept tree one by one using the /{onto}/displayconceptrelations/{iri} method with broader or narrower concept relations.") @@ -91,9 +88,9 @@ HttpEntity displaySKOSConceptHierarchyByOntology( ontologyId = ontologyId.toLowerCase(); List> rootIndividuals = null; if(TopConceptEnum.RELATIONSHIPS == topConceptIdentification) - rootIndividuals = classRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable); + rootIndividuals = skosRepository.conceptTreeWithoutTop(ontologyId,narrower,withChildren,obsoletes,lang,pageable); else - rootIndividuals = classRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable); + rootIndividuals = skosRepository.conceptTree(ontologyId,TopConceptEnum.SCHEMA == topConceptIdentification,narrower, withChildren,obsoletes,lang,pageable); StringBuilder sb = new StringBuilder(); for (TreeNode root : rootIndividuals) { sb.append(root.getIndex() + " , "+ root.getData().any().get("label").toString() + " , " + root.getData().any().get("iri").toString()).append("\n"); @@ -121,7 +118,7 @@ HttpEntity> getSKOSConceptHierarchyByOntologyAndIri( TreeNode topConcept = new TreeNode(new V2Entity(new JsonObject())); String decodedIri; decodedIri = UriUtils.decode(iri, "UTF-8"); - topConcept = classRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); + topConcept = skosRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); if (topConcept.getData().any().get("iri").toString() == null) throw new ResourceNotFoundException("No roots could be found for " + ontologyId ); @@ -150,7 +147,7 @@ HttpEntity displaySKOSConceptHierarchyByOntologyAndIri( String decodedIri; StringBuilder sb = new StringBuilder(); decodedIri = UriUtils.decode(iri, "UTF-8"); - topConcept = classRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); + topConcept = skosRepository.conceptSubTree(ontologyId, decodedIri, narrower, index, obsoletes, lang, pageable); sb.append(topConcept.getIndex() + " , "+ topConcept.getData().any().get("label").toString() + " , " + topConcept.getData().any().get("iri").toString()).append("\n"); sb.append(generateConceptHierarchyTextByOntology(topConcept, displayRelated)); @@ -176,7 +173,7 @@ public HttpEntity> findRelatedConcepts( ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - related = classRepository.findRelated(ontologyId, decodedIri, relationType.getPropertyName(),lang); + related = skosRepository.findRelated(ontologyId, decodedIri, relationType.getPropertyName(),lang); final int start = (int)pageable.getOffset(); final int end = Math.min((start + pageable.getPageSize()), related.size()); @@ -205,7 +202,7 @@ public HttpEntity displayRelatedConcepts( ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - related = classRepository.findRelated(ontologyId, decodedIri, relationType.getPropertyName(),lang); + related = skosRepository.findRelated(ontologyId, decodedIri, relationType.getPropertyName(),lang); final int start = (int)pageable.getOffset(); final int end = Math.min((start + pageable.getPageSize()), related.size()); @@ -234,7 +231,7 @@ public HttpEntity> findRelatedConceptsIndirectly( ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - related = classRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType.getPropertyName(), obsoletes,lang,pageable); + related = skosRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType.getPropertyName(), obsoletes,lang,pageable); return new ResponseEntity<>( related, HttpStatus.OK); @@ -259,7 +256,7 @@ public HttpEntity displayRelatedConceptsIndirectly( ontologyId = ontologyId.toLowerCase(); List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - related = classRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType.getPropertyName(), obsoletes,lang,pageable); + related = skosRepository.findRelatedIndirectly(ontologyId, decodedIri, relationType.getPropertyName(), obsoletes,lang,pageable); int count = 0; for (V2Entity individual : related) @@ -282,15 +279,15 @@ public HttpEntity retrieveImmediateGraph( List related = new ArrayList(); String decodedIri = UriUtils.decode(iri, "UTF-8"); - V2Entity subjectTerm = classRepository.findByOntologyAndIri(ontologyId, decodedIri, lang); + V2Entity subjectTerm = skosRepository.findByOntologyAndIri(ontologyId, decodedIri, lang); - related = classRepository.findRelated(ontologyId, decodedIri, SKOSRelation.related.getPropertyName(), lang); + related = skosRepository.findRelated(ontologyId, decodedIri, SKOSRelation.related.getPropertyName(), lang); List narrower = new ArrayList(); - narrower = classRepository.findRelated(ontologyId, decodedIri, SKOSRelation.narrower.getPropertyName(), lang); + narrower = skosRepository.findRelated(ontologyId, decodedIri, SKOSRelation.narrower.getPropertyName(), lang); List broader = new ArrayList(); - broader = classRepository.findRelated(ontologyId, decodedIri, SKOSRelation.broader.getPropertyName(), lang); + broader = skosRepository.findRelated(ontologyId, decodedIri, SKOSRelation.broader.getPropertyName(), lang); Set relatedNodes = new HashSet(); related.forEach(term -> relatedNodes.add(new Node(term.any().get("iri").toString(), term.any().get("label").toString()))); @@ -300,9 +297,9 @@ public HttpEntity retrieveImmediateGraph( broader.forEach(term -> broaderNodes.add(new Node(term.any().get("iri").toString(), term.any().get("label").toString()))); Set edges = new HashSet(); - relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "related",SKOSRelation.related.getPropertyName()))); - narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "narrower",SKOSRelation.narrower.getPropertyName()))); - broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.iri, "broader",SKOSRelation.broader.getPropertyName()))); + relatedNodes.forEach(node -> edges.add(new Edge(decodedIri, node.getIri(), "related",SKOSRelation.related.getPropertyName()))); + narrowerNodes.forEach(node -> edges.add(new Edge(decodedIri, node.getIri(), "narrower",SKOSRelation.narrower.getPropertyName()))); + broaderNodes.forEach(node -> edges.add(new Edge(decodedIri, node.getIri(), "broader",SKOSRelation.broader.getPropertyName()))); Set nodes = new HashSet(); nodes.add(new Node(decodedIri,subjectTerm.any().get("label").toString())); @@ -341,54 +338,4 @@ public StringBuilder generateConceptHierarchyTextByOntology(TreeNode r public void handleError(HttpServletRequest req, Exception exception) { } - public class Node { - String iri; - String label; - - public Node(String iri, String label) { - this.iri = iri; - this.label = label; - } - - public String getIri() { - return iri; - } - - public String getLabel() { - return label; - } - - } - - public class Edge { - String source; - String target; - String label; - String uri; - - public Edge(String source, String target, String label, String uri) { - this.source = source; - this.target = target; - this.label = label; - this.uri = uri; - } - - public String getSource() { - return source; - } - - public String getTarget() { - return target; - } - - public String getLabel() { - return label; - } - - public String getUri() { - return uri; - } - - } - } diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/model/Edge.java b/backend/src/main/java/uk/ac/ebi/spot/ols/model/Edge.java new file mode 100644 index 000000000..ba2a19984 --- /dev/null +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/model/Edge.java @@ -0,0 +1,37 @@ +package uk.ac.ebi.spot.ols.model; + +/** + * @author Erhun Giray TUNCAY + * @email giray.tuncay@tib.eu + * TIB-Leibniz Information Center for Science and Technology + */ +public class Edge { + String source; + String target; + String label; + String uri; + + public Edge(String source, String target, String label, String uri) { + this.source = source; + this.target = target; + this.label = label; + this.uri = uri; + } + + public String getSource() { + return source; + } + + public String getTarget() { + return target; + } + + public String getLabel() { + return label; + } + + public String getUri() { + return uri; + } + +} diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/model/Node.java b/backend/src/main/java/uk/ac/ebi/spot/ols/model/Node.java new file mode 100644 index 000000000..a73bba608 --- /dev/null +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/model/Node.java @@ -0,0 +1,25 @@ +package uk.ac.ebi.spot.ols.model; + +/** + * @author Erhun Giray TUNCAY + * @email giray.tuncay@tib.eu + * TIB-Leibniz Information Center for Science and Technology + */ +public class Node { + String iri; + String label; + + public Node(String iri, String label) { + this.iri = iri; + this.label = label; + } + + public String getIri() { + return iri; + } + + public String getLabel() { + return label; + } + +} diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/model/v2/SKOSRelation.java b/backend/src/main/java/uk/ac/ebi/spot/ols/model/SKOSRelation.java similarity index 96% rename from backend/src/main/java/uk/ac/ebi/spot/ols/model/v2/SKOSRelation.java rename to backend/src/main/java/uk/ac/ebi/spot/ols/model/SKOSRelation.java index 4450a8c62..f127360d6 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/model/v2/SKOSRelation.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/model/SKOSRelation.java @@ -1,4 +1,4 @@ -package uk.ac.ebi.spot.ols.model.v2; +package uk.ac.ebi.spot.ols.model; /** * @author Erhun Giray TUNCAY diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2ClassRepository.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2ClassRepository.java index 41f04cefb..74287ccd3 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2ClassRepository.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2ClassRepository.java @@ -1,10 +1,8 @@ package uk.ac.ebi.spot.ols.repository.v2; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.annotation.Primary; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.rest.webmvc.ResourceNotFoundException; @@ -19,15 +17,12 @@ import uk.ac.ebi.spot.ols.repository.Validation; import uk.ac.ebi.spot.ols.repository.transforms.LocalizationTransform; import uk.ac.ebi.spot.ols.repository.transforms.RemoveLiteralDatatypesTransform; -import uk.ac.ebi.spot.ols.repository.v1.TreeNode; import uk.ac.ebi.spot.ols.repository.v2.helpers.V2DynamicFilterParser; import uk.ac.ebi.spot.ols.repository.v2.helpers.V2SearchFieldsParser; import java.io.IOException; import java.util.*; - -import static uk.ac.ebi.spot.ols.model.v2.SKOSRelation.*; - +@Primary @Component public class V2ClassRepository { @@ -176,53 +171,6 @@ public Page getIndividualAncestorsByOntologyId(String ontologyId, Page } - - @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#schema).concat('-').concat(#narrower).concat('-').concat(#withChildren)") - public List> conceptTree (String ontologyId, boolean schema, boolean narrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable) throws IOException { - - Map> properties = new HashMap<>(); - if(!obsoletes) - properties.put("isObsolete", List.of("false")); - - List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); - - List> rootTerms = new ArrayList>(); - int count = 0; - - if(schema) { - for (V2Entity term : listOfTerms) - if (term.any().get(hasTopConcept.getPropertyName()) != null) { - for (String iriTopConcept : (ArrayList) term.any().get(hasTopConcept.getPropertyName())) { - V2Entity topConceptTerm = findTerm(listOfTerms,iriTopConcept); - TreeNode topConcept = new TreeNode(topConceptTerm); - topConcept.setIndex(String.valueOf(++count)); - if(withChildren) { - if(narrower) - populateChildrenandRelatedByNarrower(topConceptTerm,topConcept,listOfTerms); - else - populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); - } - rootTerms.add(topConcept); - } - } - } else for (V2Entity term : listOfTerms) { - TreeNode tree = new TreeNode(term); - - if (tree.isRoot() && term.any().get(topConceptOf.getPropertyName()) != null) { - tree.setIndex(String.valueOf(++count)); - if(withChildren) { - if(narrower) - populateChildrenandRelatedByNarrower(term,tree,listOfTerms); - else - populateChildrenandRelatedByBroader(term,tree,listOfTerms); - } - rootTerms.add(tree); - } - } - - return rootTerms; - } - public List allClassesOfOntology(String ontologyId, Boolean obsoletes, Pageable pageable, String lang) throws IOException { Map> properties = new HashMap<>(); if(!obsoletes) @@ -240,156 +188,6 @@ public List allClassesOfOntology(String ontologyId, Boolean obsoletes, return listOfTerms; } - @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#narrower).concat('-').concat(#withChildren)") - public List> conceptTreeWithoutTop (String ontologyId, boolean isNarrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable) throws IOException { - - List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); - - Set rootIRIs = new HashSet(); - List> rootTerms = new ArrayList>(); - int count = 0; - if(!isNarrower) { - for (V2Entity term : listOfTerms) { - if(term.any() != null && term.any().get(broader.getPropertyName()) != null) { - for (String iriBroader : getRelationsAsList(term,broader.getPropertyName())) { - V2Entity broaderTerm = findTerm(listOfTerms, iriBroader); - if (broaderTerm.any() != null && broaderTerm.any().get(broader.getPropertyName()) == null) { - rootIRIs.add(iriBroader); - } - - } - } - } - - for (String iri : rootIRIs) { - V2Entity topConceptTerm = findTerm(listOfTerms, iri); - TreeNode topConcept = new TreeNode(topConceptTerm); - topConcept.setIndex(String.valueOf(++count)); - if(withChildren) - populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); - rootTerms.add(topConcept); - } - - } else { - for (V2Entity term : listOfTerms) { - if (term.any() != null && term.any().get(narrower) != null) { - boolean root = true; - for (V2Entity V2Entity : listOfTerms) { - if (V2Entity.any() != null && V2Entity.any().get(narrower) != null) { - for (String iriNarrower : getRelationsAsList(V2Entity,narrower.getPropertyName())) { - if (term.any().get("iri").equals(iriNarrower)) - root = false; - } - } - } - - if (root) { - TreeNode topConcept = new TreeNode(term); - topConcept.setIndex(String.valueOf(++count)); - if (withChildren) - populateChildrenandRelatedByNarrower(term, topConcept, listOfTerms); - rootTerms.add(topConcept); - } - } - } - } - - return rootTerms; - } - - @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat('s').concat('-').concat(#iri).concat('-').concat(#narrower).concat('-').concat(#index)") - public TreeNode conceptSubTree(String ontologyId, String iri, boolean narrower, String index, Boolean obsoletes, String lang, Pageable pageable) throws IOException { - List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); - V2Entity topConceptTerm = findTerm(listOfTerms,iri); - TreeNode topConcept = new TreeNode(topConceptTerm); - topConcept.setIndex(index); - if(narrower) - populateChildrenandRelatedByNarrower(topConceptTerm,topConcept,listOfTerms); - else - populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); - - return topConcept; - } - - public V2Entity findTerm(List wholeList, String iri) { - for (V2Entity term : wholeList) - if(term.any().get("iri").equals(iri)) - return term; - return new V2Entity(new JsonObject()); - } - - public List findRelated(String ontologyId, String iri, String relationType, String lang) { - List related = new ArrayList(); - V2Entity term = this.findByOntologyAndIri(ontologyId, iri, lang); - if (term != null) - if (term.any().get(relationType) != null) - for (String iriBroader : getRelationsAsList(term,relationType)) - related.add(this.findByOntologyAndIri(ontologyId, iriBroader, lang)); - return related; - } - - public ListfindRelatedIndirectly(String ontologyId, String iri, String relationType, Boolean obsoletes, String lang, Pageable pageable) throws IOException { - List related = new ArrayList(); - - V2Entity V2Entity = this.findByOntologyAndIri(ontologyId, iri, lang); - if(V2Entity == null) - return related; - if(V2Entity.any().get("iri") == null) - return related; - - List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); - - for (V2Entity term : listOfTerms) { - if (term != null) - if (term.any().get(relationType) != null) - for (String iriRelated : getRelationsAsList(term,relationType)) - if(iriRelated.equals(iri)) - related.add(term); - } - - return related; - } - - public void populateChildrenandRelatedByNarrower(V2Entity term, TreeNode tree, List listOfTerms ) { - - if (term.any() != null) - for (String iriRelated : getRelationsAsList(term,related.getPropertyName())) { - TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); - related.setIndex(tree.getIndex() + ".related"); - tree.addRelated(related); - } - int count = 0; - if (term.any() != null) - for (String iriChild : getRelationsAsList(term,narrower.getPropertyName())) { - V2Entity childTerm = findTerm(listOfTerms, iriChild); - TreeNode child = new TreeNode(childTerm); - child.setIndex(tree.getIndex() + "." + ++count); - populateChildrenandRelatedByNarrower(childTerm, child, listOfTerms); - tree.addChild(child); - } - } - - public void populateChildrenandRelatedByBroader(V2Entity term, TreeNode tree, List listOfTerms) { - if (term.any() != null) - for (String iriRelated : getRelationsAsList(term,related.getPropertyName())) { - TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); - related.setIndex(tree.getIndex() + ".related"); - tree.addRelated(related); - } - int count = 0; - for ( V2Entity V2Entity : listOfTerms) { - if (V2Entity.any() != null) - for (String iriBroader : getRelationsAsList(V2Entity,broader.getPropertyName())) - if(term.any().get("iri") != null) - if (term.any().get("iri").equals(iriBroader)) { - TreeNode child = new TreeNode(V2Entity); - child.setIndex(tree.getIndex()+"."+ ++count); - populateChildrenandRelatedByBroader(V2Entity,child,listOfTerms); - tree.addChild(child); - } - } - } - public List getRelationsAsList(V2Entity entity, String relationType){ if(entity.any().get(relationType) instanceof String) return Arrays.asList((String) entity.any().get(relationType)); diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2SKOSRepository.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2SKOSRepository.java new file mode 100644 index 000000000..f20f4f183 --- /dev/null +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/v2/V2SKOSRepository.java @@ -0,0 +1,217 @@ +package uk.ac.ebi.spot.ols.repository.v2; + +import com.google.gson.JsonObject; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; +import uk.ac.ebi.spot.ols.model.v2.V2Entity; +import uk.ac.ebi.spot.ols.repository.v1.TreeNode; + +import java.io.IOException; +import java.util.*; + +import static uk.ac.ebi.spot.ols.model.SKOSRelation.*; + +/** + * @author Erhun Giray TUNCAY + * @email giray.tuncay@tib.eu + * TIB-Leibniz Information Center for Science and Technology + */ +@Component +public class V2SKOSRepository extends V2ClassRepository { + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#schema).concat('-').concat(#narrower).concat('-').concat(#withChildren)") + public List> conceptTree (String ontologyId, boolean schema, boolean narrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable) throws IOException { + + Map> properties = new HashMap<>(); + if(!obsoletes) + properties.put("isObsolete", List.of("false")); + + List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); + List> rootTerms = new ArrayList>(); + int count = 0; + + if(schema) { + for (V2Entity term : listOfTerms) + if (term.any().get(hasTopConcept.getPropertyName()) != null) { + for (String iriTopConcept : (ArrayList) term.any().get(hasTopConcept.getPropertyName())) { + V2Entity topConceptTerm = findTerm(listOfTerms,iriTopConcept); + TreeNode topConcept = new TreeNode(topConceptTerm); + topConcept.setIndex(String.valueOf(++count)); + if(withChildren) { + if(narrower) + populateChildrenandRelatedByNarrower(topConceptTerm,topConcept,listOfTerms); + else + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); + } + rootTerms.add(topConcept); + } + } + } else for (V2Entity term : listOfTerms) { + TreeNode tree = new TreeNode(term); + + if (tree.isRoot() && term.any().get(topConceptOf.getPropertyName()) != null) { + tree.setIndex(String.valueOf(++count)); + if(withChildren) { + if(narrower) + populateChildrenandRelatedByNarrower(term,tree,listOfTerms); + else + populateChildrenandRelatedByBroader(term,tree,listOfTerms); + } + rootTerms.add(tree); + } + } + + return rootTerms; + } + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat(#narrower).concat('-').concat(#withChildren)") + public List> conceptTreeWithoutTop (String ontologyId, boolean isNarrower, boolean withChildren, Boolean obsoletes, String lang, Pageable pageable) throws IOException { + + List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); + + Set rootIRIs = new HashSet(); + List> rootTerms = new ArrayList>(); + int count = 0; + if(!isNarrower) { + for (V2Entity term : listOfTerms) { + if(term.any() != null && term.any().get(broader.getPropertyName()) != null) { + for (String iriBroader : getRelationsAsList(term,broader.getPropertyName())) { + V2Entity broaderTerm = findTerm(listOfTerms, iriBroader); + if (broaderTerm.any() != null && broaderTerm.any().get(broader.getPropertyName()) == null) { + rootIRIs.add(iriBroader); + } + + } + } + } + + for (String iri : rootIRIs) { + V2Entity topConceptTerm = findTerm(listOfTerms, iri); + TreeNode topConcept = new TreeNode(topConceptTerm); + topConcept.setIndex(String.valueOf(++count)); + if(withChildren) + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); + rootTerms.add(topConcept); + } + + } else { + for (V2Entity term : listOfTerms) { + if (term.any() != null && term.any().get(narrower) != null) { + boolean root = true; + for (V2Entity V2Entity : listOfTerms) { + if (V2Entity.any() != null && V2Entity.any().get(narrower) != null) { + for (String iriNarrower : getRelationsAsList(V2Entity,narrower.getPropertyName())) { + if (term.any().get("iri").equals(iriNarrower)) + root = false; + } + } + } + + if (root) { + TreeNode topConcept = new TreeNode(term); + topConcept.setIndex(String.valueOf(++count)); + if (withChildren) + populateChildrenandRelatedByNarrower(term, topConcept, listOfTerms); + rootTerms.add(topConcept); + } + } + } + } + + return rootTerms; + } + + @Cacheable(value = "concepttree", key="#ontologyId.concat('-').concat('s').concat('-').concat(#iri).concat('-').concat(#narrower).concat('-').concat(#index)") + public TreeNode conceptSubTree(String ontologyId, String iri, boolean narrower, String index, Boolean obsoletes, String lang, Pageable pageable) throws IOException { + List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); + V2Entity topConceptTerm = findTerm(listOfTerms,iri); + TreeNode topConcept = new TreeNode(topConceptTerm); + topConcept.setIndex(index); + if(narrower) + populateChildrenandRelatedByNarrower(topConceptTerm,topConcept,listOfTerms); + else + populateChildrenandRelatedByBroader(topConceptTerm,topConcept,listOfTerms); + + return topConcept; + } + + public V2Entity findTerm(List wholeList, String iri) { + for (V2Entity term : wholeList) + if(term.any().get("iri").equals(iri)) + return term; + return new V2Entity(new JsonObject()); + } + + public List findRelated(String ontologyId, String iri, String relationType, String lang) { + List related = new ArrayList(); + V2Entity term = this.findByOntologyAndIri(ontologyId, iri, lang); + if (term != null) + if (term.any().get(relationType) != null) + for (String iriBroader : getRelationsAsList(term,relationType)) + related.add(this.findByOntologyAndIri(ontologyId, iriBroader, lang)); + return related; + } + + public ListfindRelatedIndirectly(String ontologyId, String iri, String relationType, Boolean obsoletes, String lang, Pageable pageable) throws IOException { + List related = new ArrayList(); + + V2Entity V2Entity = this.findByOntologyAndIri(ontologyId, iri, lang); + if(V2Entity == null) + return related; + if(V2Entity.any().get("iri") == null) + return related; + + List listOfTerms = allClassesOfOntology(ontologyId, obsoletes, pageable, lang); + + for (V2Entity term : listOfTerms) { + if (term != null) + if (term.any().get(relationType) != null) + for (String iriRelated : getRelationsAsList(term,relationType)) + if(iriRelated.equals(iri)) + related.add(term); + } + + return related; + } + + public void populateChildrenandRelatedByNarrower(V2Entity term, TreeNode tree, List listOfTerms ) { + + if (term.any() != null) + for (String iriRelated : getRelationsAsList(term,related.getPropertyName())) { + TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); + related.setIndex(tree.getIndex() + ".related"); + tree.addRelated(related); + } + int count = 0; + if (term.any() != null) + for (String iriChild : getRelationsAsList(term,narrower.getPropertyName())) { + V2Entity childTerm = findTerm(listOfTerms, iriChild); + TreeNode child = new TreeNode(childTerm); + child.setIndex(tree.getIndex() + "." + ++count); + populateChildrenandRelatedByNarrower(childTerm, child, listOfTerms); + tree.addChild(child); + } + } + + public void populateChildrenandRelatedByBroader(V2Entity term, TreeNode tree, List listOfTerms) { + if (term.any() != null) + for (String iriRelated : getRelationsAsList(term,related.getPropertyName())) { + TreeNode related = new TreeNode(findTerm(listOfTerms, iriRelated)); + related.setIndex(tree.getIndex() + ".related"); + tree.addRelated(related); + } + int count = 0; + for ( V2Entity V2Entity : listOfTerms) { + if (V2Entity.any() != null) + for (String iriBroader : getRelationsAsList(V2Entity,broader.getPropertyName())) + if(term.any().get("iri") != null) + if (term.any().get("iri").equals(iriBroader)) { + TreeNode child = new TreeNode(V2Entity); + child.setIndex(tree.getIndex()+"."+ ++count); + populateChildrenandRelatedByBroader(V2Entity,child,listOfTerms); + tree.addChild(child); + } + } + } +} From 6afd55ff7f6d1155267ac353f1df51c259c46a3c Mon Sep 17 00:00:00 2001 From: Erhun Giray TUNCAY <48091473+giraygi@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:47:59 +0200 Subject: [PATCH 15/15] reverted the get first method of olssolrclient --- .../uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java index c2cd47052..6168d4f31 100644 --- a/backend/src/main/java/uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java +++ b/backend/src/main/java/uk/ac/ebi/spot/ols/repository/solr/OlsSolrClient.java @@ -96,8 +96,8 @@ public JsonElement getFirst(OlsSolrQuery query) { if(qr.getResults().getNumFound() < 1) { logger.debug("Expected at least 1 result for solr getFirst for solr query = {}", query.constructQuery().jsonStr()); - return new JsonObject(); - //throw new RuntimeException("Expected at least 1 result for solr getFirst"); + //return new JsonObject(); + throw new RuntimeException("Expected at least 1 result for solr getFirst"); } return getOlsEntityFromSolrResult(qr.getResults().get(0));