From f624775d0996a0f8398c734a146031a4bee01a08 Mon Sep 17 00:00:00 2001 From: Luke Sikina Date: Fri, 11 Oct 2024 09:08:05 -0400 Subject: [PATCH] Lint things --- .github/workflows/linting.yml | 31 ++ .github/workflows/maven.yml | 2 +- README.md | 58 +-- .../dictionary/DictionaryApplication.java | 6 +- .../dictionary/concept/ConceptController.java | 33 +- .../concept/ConceptFilterQueryGenerator.java | 89 ++-- .../dictionary/concept/ConceptRepository.java | 224 +++++----- .../concept/ConceptResultSetExtractor.java | 10 +- .../concept/ConceptResultSetUtil.java | 18 +- .../dictionary/concept/ConceptService.java | 18 +- .../dictionary/concept/model/ConceptType.java | 3 +- .../dictionary/dashboard/DashboardConfig.java | 6 +- .../dashboard/DashboardRepository.java | 84 ++-- .../DashboardRowResultSetExtractor.java | 3 +- .../dashboard/DashboardService.java | 26 +- .../dbmi/avillach/dictionary/facet/Facet.java | 5 +- .../dictionary/facet/FacetCategory.java | 5 +- .../facet/FacetCategoryExtractor.java | 26 +- .../dictionary/facet/FacetController.java | 9 +- .../dictionary/facet/FacetMapper.java | 10 +- .../dictionary/facet/FacetQueryGenerator.java | 385 +++++++++--------- .../dictionary/facet/FacetRepository.java | 118 +++--- .../dictionary/facet/FacetService.java | 3 +- .../dictionary/facet/FilterPreProcessor.java | 11 +- .../dictionary/filter/QueryParamPair.java | 3 +- .../dictionary/info/InfoResponse.java | 2 +- .../DictionaryApplicationTests.java | 5 +- .../dictionary/TestDictionaryApplication.java | 16 +- .../concept/ConceptControllerTest.java | 55 +-- .../concept/ConceptResultSetUtilTest.java | 2 +- .../concept/ConceptServiceTest.java | 49 +-- .../dictionary/concept/model/ConceptTest.java | 11 +- .../concept/model/ConceptTypeTest.java | 2 +- .../dashboard/DashboardConfigTest.java | 8 +- .../dashboard/DashboardControllerTest.java | 5 +- .../dashboard/DashboardRepositoryTest.java | 51 ++- .../dashboard/DashboardServiceTest.java | 5 +- .../dictionary/facet/FacetControllerTest.java | 20 +- .../facet/FacetQueryGeneratorTest.java | 137 ++----- .../dictionary/facet/FacetServiceTest.java | 11 +- .../facet/FilterPreProcessorTest.java | 10 +- .../ConceptFilterQueryGeneratorTest.java | 29 +- .../dictionary/info/InfoControllerTest.java | 2 +- 43 files changed, 710 insertions(+), 896 deletions(-) create mode 100644 .github/workflows/linting.yml diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..8fe5694 --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,31 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Lint Project + +on: + push: + branches: [ "main", "release" ] + pull_request: + branches: [ "main", "release" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + - name: Lint Project + run: mvn spotless:check diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 3fc1e80..ee74df2 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -6,7 +6,7 @@ # separate terms of service, privacy policy, and support # documentation. -name: Java CI with Maven +name: Build and Run Tests on: push: diff --git a/README.md b/README.md index baf3062..99781ee 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,20 @@ The Dictionary Project's goal is to create a unified process for enabling the fi of a query. It needs to work across our current projects, and, in anticipation of our push to productize PIC-SURE, it needs to be workable for new PIC-SURE installations. +## Contribution + +### Requirements + +- Java 21 +- Maven + +### Setup + +1. Copy the linting commit hook: +`cp code-formatting/pre-commit.sh .git/hooks/pre-commit` +2. Do a clean build of the project: +`mvn clean install` + ## Usage ### Create Env File @@ -39,47 +53,3 @@ docker exec -ti dictionary-db psql -U picsure dictionary ```shell docker compose up -d --build ``` - -## Solution - -### UI - -The Dictionary Project's end result will be an overhaul of the PIC-SURE Query Builder tab. It will provide the user with -three ways to find variables they wish to query on: - -- An expandable tree of variables and their concept paths -- A series of facets on the page's left gutter -- A search bar - -The tree of variables can be filtered using the facets or the search bar. Users might also need additional information -to support the filtering options available to them. The UI will have modals that provide meta information about variables, facets, facet categories, and, in relevant environments, datasets. - -### API - -The frontend will be supported by a new Dictionary API. The API will have four core resources: - -- Concepts (`/concepts/`): Variables, the concept paths to them, and associated metadata. The concepts API will support listing (`/concepts/`), viewing details (`/concepts//`), and viewing a tree (`/concepts/tree//?depth=[1-9]+`). The listing API will return a paginated list of concepts; the response will also be filtered using a universal filter object. That object will be shared among all API endpoints, and will be - described later. The tree API returns a hierarchy of concept nodes descending from the requested node, limiting the response to a depth of `depth`. The details API will be used for viewing details such as meta fields, type information, related facets, and related harmonized variables. -- Facets (`/facets/`): Facets, their categories, and associated metadata. The facets API will support listing (`/facets/`), and viewing details (`/facets//`). The listing API will be filtered using the universal filter object. While facets are technically two-dimensional, their hierarchies are quite shallow and broad, so a traditional listing structure will be used, with no pagination. The details API will be used for viewing meta details, related facets, and potentially variable counts for a facet. -- datasets (`/datasets/`): datasets, and their metadata. There datasets API will support viewing details (`/datasets/`) -- updates (`/updates/`): update all business objects. This will not be a published, user facing API to start. Instead, it will be for ETL purposes. In the future, we may allow admin users access to this API so that they can update dictionaries in live environments. - -The universal filter object can be `POST`ed to filterable listing APIs to filter results. It has the following structure: - -```json -{ - "search": "search terms", - "facets": [ - { - "facet_category": "facet title", - "facet_id": "facet id" - } - ] -} -``` - -### Architecture - -The Dictionary project has a UI, API, database, and an ETL. The UI will be part of the unified, next generation monolith currently in progress. The API will be isolated to its own docker container, reachable via the proxy endpoints in the PIC-SURE API. The database will be handled on a per-project basis; in some instances it will make more sense to isolate the database to its own container, but in others, it will remain part of the PIC-SURE relational database monolith. - -The queries we will be making to the relational database are in many instances complex, and well outside the standards set out by SQL. This means that this project will likely be locked into whatever database we choose, as the tech debt for moving to a different RDMS would not be justifiable. We would like to use Postgres for this project. \ No newline at end of file diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/DictionaryApplication.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/DictionaryApplication.java index 436e399..c12b6eb 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/DictionaryApplication.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/DictionaryApplication.java @@ -6,8 +6,8 @@ @SpringBootApplication public class DictionaryApplication { - public static void main(String[] args) { - SpringApplication.run(DictionaryApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(DictionaryApplication.class, args); + } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptController.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptController.java index 8da6fd8..d2c5c1c 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptController.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptController.java @@ -22,25 +22,19 @@ public class ConceptController { private Integer MAX_DEPTH; - public ConceptController( - @Autowired ConceptService conceptService - ) { + public ConceptController(@Autowired ConceptService conceptService) { this.conceptService = conceptService; } @PostMapping(path = "/concepts") public ResponseEntity> listConcepts( - @RequestBody Filter filter, - @RequestParam(name = "page_number", defaultValue = "0", required = false) int page, + @RequestBody Filter filter, @RequestParam(name = "page_number", defaultValue = "0", required = false) int page, @RequestParam(name = "page_size", defaultValue = "10", required = false) int size ) { PageRequest pagination = PageRequest.of(page, size); - PageImpl pageResp = new PageImpl<>( - conceptService.listConcepts(filter, pagination), - pagination, - conceptService.countConcepts(filter) - ); + PageImpl pageResp = + new PageImpl<>(conceptService.listConcepts(filter, pagination), pagination, conceptService.countConcepts(filter)); return ResponseEntity.ok(pageResp); } @@ -52,8 +46,7 @@ public ResponseEntity> dumpConcepts( ) { PageRequest pagination = PageRequest.of(page, size); PageImpl pageResp = new PageImpl<>( - conceptService.listDetailedConcepts(new Filter(List.of(), "", List.of()), pagination), - pagination, + conceptService.listDetailedConcepts(new Filter(List.of(), "", List.of()), pagination), pagination, conceptService.countConcepts(new Filter(List.of(), "", List.of())) ); @@ -61,26 +54,18 @@ public ResponseEntity> dumpConcepts( } @PostMapping(path = "/concepts/detail/{dataset}") - public ResponseEntity conceptDetail( - @PathVariable(name = "dataset") String dataset, - @RequestBody() String conceptPath - ) { - return conceptService.conceptDetail(dataset, conceptPath) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + public ResponseEntity conceptDetail(@PathVariable(name = "dataset") String dataset, @RequestBody() String conceptPath) { + return conceptService.conceptDetail(dataset, conceptPath).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build()); } @PostMapping(path = "/concepts/tree/{dataset}") public ResponseEntity conceptTree( - @PathVariable(name = "dataset") String dataset, - @RequestBody() String conceptPath, + @PathVariable(name = "dataset") String dataset, @RequestBody() String conceptPath, @RequestParam(name = "depth", required = false, defaultValue = "2") Integer depth ) { if (depth < 0 || depth > MAX_DEPTH) { return ResponseEntity.badRequest().build(); } - return conceptService.conceptTree(dataset, conceptPath, depth) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + return conceptService.conceptTree(dataset, conceptPath, depth).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build()); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptFilterQueryGenerator.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptFilterQueryGenerator.java index 5f078d2..5388c40 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptFilterQueryGenerator.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptFilterQueryGenerator.java @@ -34,40 +34,35 @@ AND concept_node_meta.KEY IN (:disallowed_meta_keys) """; private static final String CONSENT_QUERY = """ - dataset.dataset_id IN ( - SELECT - consent.dataset_id - FROM consent - LEFT JOIN dataset ON dataset.dataset_id = consent.dataset_id - WHERE - concat(dataset.ref, '.', consent.consent_code) IN (:consents) OR - (dataset.ref IN (:consents) AND consent.consent_code = '') - UNION - SELECT - dataset_harmonization.harmonized_dataset_id - FROM consent - JOIN dataset_harmonization ON dataset_harmonization.source_dataset_id = consent.dataset_id - LEFT JOIN dataset ON dataset.dataset_id = dataset_harmonization.source_dataset_id - WHERE - concat(dataset.ref, '.', consent.consent_code) IN (:consents) OR - (dataset.ref IN (:consents) AND consent.consent_code = '') - ) AND - """; + dataset.dataset_id IN ( + SELECT + consent.dataset_id + FROM consent + LEFT JOIN dataset ON dataset.dataset_id = consent.dataset_id + WHERE + concat(dataset.ref, '.', consent.consent_code) IN (:consents) OR + (dataset.ref IN (:consents) AND consent.consent_code = '') + UNION + SELECT + dataset_harmonization.harmonized_dataset_id + FROM consent + JOIN dataset_harmonization ON dataset_harmonization.source_dataset_id = consent.dataset_id + LEFT JOIN dataset ON dataset.dataset_id = dataset_harmonization.source_dataset_id + WHERE + concat(dataset.ref, '.', consent.consent_code) IN (:consents) OR + (dataset.ref IN (:consents) AND consent.consent_code = '') + ) AND + """; @Autowired - public ConceptFilterQueryGenerator( - @Value("${filtering.unfilterable_concepts}") List disallowedMetaFields - ) { + public ConceptFilterQueryGenerator(@Value("${filtering.unfilterable_concepts}") List disallowedMetaFields) { this.disallowedMetaFields = disallowedMetaFields; } /** - * This generates a query that will return a list of concept_node IDs for the given filter. - *

- * A filter object has a list of facets, each belonging to a category. Within a category, - * facets are ORed together. Between categories, facets are ANDed together. - * In SQL, this is represented as N clauses for N facets, all INTERSECTed together. Search - * also acts as its own special facet here. + * This generates a query that will return a list of concept_node IDs for the given filter.

A filter object has a list of facets, + * each belonging to a category. Within a category, facets are ORed together. Between categories, facets are ANDed together. In SQL, + * this is represented as N clauses for N facets, all INTERSECTed together. Search also acts as its own special facet here. * * @param filter universal filter object for the page * @param pageable pagination, if applicable @@ -113,8 +108,7 @@ ORDER BY max((1 + rank) * coalesce(rank_adjustment, 1)) DESC, q.concept_node_id LIMIT :limit OFFSET :offset """; - params.addValue("limit", pageable.getPageSize()) - .addValue("offset", pageable.getOffset()); + params.addValue("limit", pageable.getPageSize()).addValue("offset", pageable.getOffset()); } superQuery = " concepts_filtered_sorted AS (\n" + superQuery + "\n)"; @@ -150,26 +144,26 @@ private String createValuelessNodeFilter(String search, List consents) { continuous_max.value <> '' OR categorical_values.value <> '' ) - """.formatted(rankQuery, rankWhere, consentWhere); + """ + .formatted(rankQuery, rankWhere, consentWhere); } private List createFacetFilter(Filter filter, MapSqlParameterSource params) { String consentWhere = CollectionUtils.isEmpty(filter.consents()) ? "" : CONSENT_QUERY; - return filter.facets().stream() - .collect(Collectors.groupingBy(Facet::category)) - .entrySet().stream() - .map(facetsForCategory -> { - params - // The templating here is to namespace the params for each facet category in the query - .addValue("facets_for_category_%s".formatted(facetsForCategory.getKey()), facetsForCategory.getValue().stream().map(Facet::name).toList()) - .addValue("category_%s".formatted(facetsForCategory.getKey()), facetsForCategory.getKey()); - String rankQuery = "0"; - String rankWhere = ""; - if (StringUtils.hasLength(filter.search())) { - rankQuery = "ts_rank(searchable_fields, (phraseto_tsquery(:search)::text || ':*')::tsquery)"; - rankWhere = "concept_node.searchable_fields @@ (phraseto_tsquery(:search)::text || ':*')::tsquery AND"; - } - return """ + return filter.facets().stream().collect(Collectors.groupingBy(Facet::category)).entrySet().stream().map(facetsForCategory -> { + params + // The templating here is to namespace the params for each facet category in the query + .addValue( + "facets_for_category_%s".formatted(facetsForCategory.getKey()), + facetsForCategory.getValue().stream().map(Facet::name).toList() + ).addValue("category_%s".formatted(facetsForCategory.getKey()), facetsForCategory.getKey()); + String rankQuery = "0"; + String rankWhere = ""; + if (StringUtils.hasLength(filter.search())) { + rankQuery = "ts_rank(searchable_fields, (phraseto_tsquery(:search)::text || ':*')::tsquery)"; + rankWhere = "concept_node.searchable_fields @@ (phraseto_tsquery(:search)::text || ':*')::tsquery AND"; + } + return """ ( SELECT facet__concept_node.concept_node_id AS concept_node_id, @@ -187,8 +181,7 @@ facet.name IN (:facets_for_category_%s ) AND facet_category.name = :category_%s facet__concept_node.concept_node_id ) """.formatted(rankQuery, rankWhere, consentWhere, facetsForCategory.getKey(), facetsForCategory.getKey()); - }) - .toList(); + }).toList(); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java index 5832f86..410e379 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java @@ -58,27 +58,28 @@ public ConceptRepository( public List getConcepts(Filter filter, Pageable pageable) { QueryParamPair filterQ = filterGen.generateFilterQuery(filter, pageable); - String sql = ALLOW_FILTERING_Q + ", " + filterQ.query() + """ - SELECT - concept_node.*, - ds.REF as dataset, - ds.abbreviation AS studyAcronym, - continuous_min.VALUE as min, continuous_max.VALUE as max, - categorical_values.VALUE as values, - allow_filtering.allowFiltering AS allowFiltering, - meta_description.VALUE AS description - FROM - concept_node - INNER JOIN concepts_filtered_sorted ON concepts_filtered_sorted.concept_node_id = concept_node.concept_node_id - LEFT JOIN dataset AS ds ON concept_node.dataset_id = ds.dataset_id - LEFT JOIN concept_node_meta AS meta_description ON concept_node.concept_node_id = meta_description.concept_node_id AND meta_description.KEY = 'description' - LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' - LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' - LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' - LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id - ORDER BY - concepts_filtered_sorted.rank DESC, concept_node.concept_node_id ASC - """; + String sql = ALLOW_FILTERING_Q + ", " + filterQ.query() + + """ + SELECT + concept_node.*, + ds.REF as dataset, + ds.abbreviation AS studyAcronym, + continuous_min.VALUE as min, continuous_max.VALUE as max, + categorical_values.VALUE as values, + allow_filtering.allowFiltering AS allowFiltering, + meta_description.VALUE AS description + FROM + concept_node + INNER JOIN concepts_filtered_sorted ON concepts_filtered_sorted.concept_node_id = concept_node.concept_node_id + LEFT JOIN dataset AS ds ON concept_node.dataset_id = ds.dataset_id + LEFT JOIN concept_node_meta AS meta_description ON concept_node.concept_node_id = meta_description.concept_node_id AND meta_description.KEY = 'description' + LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' + LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' + LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' + LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id + ORDER BY + concepts_filtered_sorted.rank DESC, concept_node.concept_node_id ASC + """; MapSqlParameterSource params = filterQ.params().addValue("disallowed_meta_keys", disallowedMetaFields); return template.query(sql, params, mapper); @@ -92,30 +93,29 @@ public long countConcepts(Filter filter) { } public Optional getConcept(String dataset, String conceptPath) { - String sql = ALLOW_FILTERING_Q + """ - SELECT - concept_node.*, - ds.REF as dataset, - ds.abbreviation AS studyAcronym, - continuous_min.VALUE as min, continuous_max.VALUE as max, - categorical_values.VALUE as values, - allow_filtering.allowFiltering AS allowFiltering, - meta_description.VALUE AS description - FROM - concept_node - LEFT JOIN dataset AS ds ON concept_node.dataset_id = ds.dataset_id - LEFT JOIN concept_node_meta AS meta_description ON concept_node.concept_node_id = meta_description.concept_node_id AND meta_description.KEY = 'description' - LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' - LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' - LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' - LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id - WHERE - concept_node.concept_path = :conceptPath - AND ds.REF = :dataset - """; - MapSqlParameterSource params = new MapSqlParameterSource() - .addValue("conceptPath", conceptPath) - .addValue("dataset", dataset) + String sql = ALLOW_FILTERING_Q + + """ + SELECT + concept_node.*, + ds.REF as dataset, + ds.abbreviation AS studyAcronym, + continuous_min.VALUE as min, continuous_max.VALUE as max, + categorical_values.VALUE as values, + allow_filtering.allowFiltering AS allowFiltering, + meta_description.VALUE AS description + FROM + concept_node + LEFT JOIN dataset AS ds ON concept_node.dataset_id = ds.dataset_id + LEFT JOIN concept_node_meta AS meta_description ON concept_node.concept_node_id = meta_description.concept_node_id AND meta_description.KEY = 'description' + LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' + LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' + LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' + LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id + WHERE + concept_node.concept_path = :conceptPath + AND ds.REF = :dataset + """; + MapSqlParameterSource params = new MapSqlParameterSource().addValue("conceptPath", conceptPath).addValue("dataset", dataset) .addValue("disallowed_meta_keys", disallowedMetaFields); return template.query(sql, params, mapper).stream().findFirst(); } @@ -132,9 +132,7 @@ public Map getConceptMeta(String dataset, String conceptPath) { concept_node.concept_path = :conceptPath AND dataset.REF = :dataset """; - MapSqlParameterSource params = new MapSqlParameterSource() - .addValue("conceptPath", conceptPath) - .addValue("dataset", dataset); + MapSqlParameterSource params = new MapSqlParameterSource().addValue("conceptPath", conceptPath).addValue("dataset", dataset); return template.query(sql, params, new MapExtractor("KEY", "VALUE")); } @@ -151,9 +149,7 @@ public Map> getConceptMetaForConcepts(List (concept_node.CONCEPT_PATH, dataset.REF) IN (:pairs) ORDER BY concept_node.CONCEPT_PATH, dataset.REF """; - List pairs = concepts.stream() - .map(c -> new String[]{c.conceptPath(), c.dataset()}) - .toList(); + List pairs = concepts.stream().map(c -> new String[] {c.conceptPath(), c.dataset()}).toList(); MapSqlParameterSource params = new MapSqlParameterSource().addValue("pairs", pairs); return template.query(sql, params, conceptMetaExtractor); @@ -161,83 +157,81 @@ public Map> getConceptMetaForConcepts(List } public Optional getConceptTree(String dataset, String conceptPath, int depth) { - String sql = ALLOW_FILTERING_Q + """ - , core_query AS ( - WITH RECURSIVE nodes AS ( + String sql = ALLOW_FILTERING_Q + + """ + , core_query AS ( + WITH RECURSIVE nodes AS ( + SELECT + concept_node_id, parent_id, 0 AS depth + FROM + concept_node + LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + WHERE + concept_node.CONCEPT_PATH = :path + AND dataset.REF = :dataset + UNION + SELECT + child_nodes.concept_node_id, child_nodes.parent_id, parent_node.depth+ 1 + FROM + concept_node child_nodes + INNER JOIN nodes parent_node ON child_nodes.parent_id = parent_node.concept_node_id + LEFT JOIN dataset ON child_nodes.dataset_id = dataset.dataset_id + ) SELECT - concept_node_id, parent_id, 0 AS depth + depth, child_nodes.concept_node_id + FROM + nodes parent_node + INNER JOIN concept_node child_nodes ON child_nodes.parent_id = parent_node.concept_node_id + WHERE + depth < :depth + UNION + SELECT + 0 as depth, concept_node.concept_node_id FROM concept_node LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id WHERE concept_node.CONCEPT_PATH = :path AND dataset.REF = :dataset - UNION + UNION SELECT - child_nodes.concept_node_id, child_nodes.parent_id, parent_node.depth+ 1 + -1 as depth, concept_node.concept_node_id FROM - concept_node child_nodes - INNER JOIN nodes parent_node ON child_nodes.parent_id = parent_node.concept_node_id - LEFT JOIN dataset ON child_nodes.dataset_id = dataset.dataset_id + concept_node + WHERE + concept_node.concept_node_id = ( + SELECT + parent_id + FROM + concept_node + LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + WHERE + concept_node.CONCEPT_PATH = :path + AND dataset.REF = :dataset + ) + ORDER BY depth ASC ) SELECT - depth, child_nodes.concept_node_id - FROM - nodes parent_node - INNER JOIN concept_node child_nodes ON child_nodes.parent_id = parent_node.concept_node_id - WHERE - depth < :depth - UNION - SELECT - 0 as depth, concept_node.concept_node_id + concept_node.*, + ds.REF AS dataset, + ds.abbreviation AS studyAcronym, + continuous_min.VALUE AS min, continuous_max.VALUE AS max, + categorical_values.VALUE AS values, + meta_description.VALUE AS description, + allow_filtering.allowFiltering AS allowFiltering, + core_query.depth AS depth FROM concept_node - LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id - WHERE - concept_node.CONCEPT_PATH = :path - AND dataset.REF = :dataset - UNION - SELECT - -1 as depth, concept_node.concept_node_id - FROM - concept_node - WHERE - concept_node.concept_node_id = ( - SELECT - parent_id - FROM - concept_node - LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id - WHERE - concept_node.CONCEPT_PATH = :path - AND dataset.REF = :dataset - ) - ORDER BY depth ASC - ) - SELECT - concept_node.*, - ds.REF AS dataset, - ds.abbreviation AS studyAcronym, - continuous_min.VALUE AS min, continuous_max.VALUE AS max, - categorical_values.VALUE AS values, - meta_description.VALUE AS description, - allow_filtering.allowFiltering AS allowFiltering, - core_query.depth AS depth - FROM - concept_node - INNER JOIN core_query ON concept_node.concept_node_id = core_query.concept_node_id - LEFT JOIN dataset AS ds ON concept_node.dataset_id = ds.dataset_id - LEFT JOIN concept_node_meta AS meta_description ON concept_node.concept_node_id = meta_description.concept_node_id AND meta_description.KEY = 'description' - LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' - LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' - LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' - LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id - """; - MapSqlParameterSource params = new MapSqlParameterSource() - .addValue("path", conceptPath) - .addValue("dataset", dataset) - .addValue("depth", depth) - .addValue("disallowed_meta_keys", disallowedMetaFields); + INNER JOIN core_query ON concept_node.concept_node_id = core_query.concept_node_id + LEFT JOIN dataset AS ds ON concept_node.dataset_id = ds.dataset_id + LEFT JOIN concept_node_meta AS meta_description ON concept_node.concept_node_id = meta_description.concept_node_id AND meta_description.KEY = 'description' + LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' + LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' + LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' + LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id + """; + MapSqlParameterSource params = new MapSqlParameterSource().addValue("path", conceptPath).addValue("dataset", dataset) + .addValue("depth", depth).addValue("disallowed_meta_keys", disallowedMetaFields); if (depth < 0) { return Optional.empty(); diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetExtractor.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetExtractor.java index ce85966..95d1f2d 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetExtractor.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetExtractor.java @@ -16,7 +16,8 @@ public class ConceptResultSetExtractor implements ResultSetExtractor { @Autowired private ConceptResultSetUtil conceptResultSetUtil; - private record ConceptWithId(Concept c, int id) {}; + private record ConceptWithId(Concept c, int id) { + }; @Override public Concept extractData(ResultSet rs) throws SQLException, DataAccessException { @@ -28,7 +29,9 @@ public Concept extractData(ResultSet rs) throws SQLException, DataAccessExceptio case Continuous -> conceptResultSetUtil.mapContinuous(rs); }; ConceptWithId conceptWithId = new ConceptWithId(c, rs.getInt("concept_node_id")); - if (root == null) { root = conceptWithId; } + if (root == null) { + root = conceptWithId; + } int parentId = rs.getInt("parent_id"); // weirdness: null value for int is 0, so to check for missing parent value, you need the wasNull check @@ -45,8 +48,7 @@ public Concept extractData(ResultSet rs) throws SQLException, DataAccessExceptio private Concept seedChildren(ConceptWithId root, Map> conceptsByParentId) { List children = conceptsByParentId.getOrDefault(root.id, List.of()).stream() - .map(conceptWithId -> seedChildren(conceptWithId, conceptsByParentId)) - .toList(); + .map(conceptWithId -> seedChildren(conceptWithId, conceptsByParentId)).toList(); return root.c.withChildren(children); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtil.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtil.java index d2690e9..9e8c220 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtil.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtil.java @@ -23,23 +23,17 @@ public class ConceptResultSetUtil { public CategoricalConcept mapCategorical(ResultSet rs) throws SQLException { return new CategoricalConcept( - rs.getString("concept_path"), rs.getString("name"), - rs.getString("display"), rs.getString("dataset"), rs.getString("description"), - rs.getString("values") == null ? List.of() : parseValues(rs.getString("values")), - rs.getBoolean("allowFiltering"), rs.getString("studyAcronym"), - null, - null + rs.getString("concept_path"), rs.getString("name"), rs.getString("display"), rs.getString("dataset"), + rs.getString("description"), rs.getString("values") == null ? List.of() : parseValues(rs.getString("values")), + rs.getBoolean("allowFiltering"), rs.getString("studyAcronym"), null, null ); } public ContinuousConcept mapContinuous(ResultSet rs) throws SQLException { return new ContinuousConcept( - rs.getString("concept_path"), rs.getString("name"), - rs.getString("display"), rs.getString("dataset"), rs.getString("description"), - rs.getBoolean("allowFiltering"), - parseMin(rs.getString("values")), parseMax(rs.getString("values")), - rs.getString("studyAcronym"), - null + rs.getString("concept_path"), rs.getString("name"), rs.getString("display"), rs.getString("dataset"), + rs.getString("description"), rs.getBoolean("allowFiltering"), parseMin(rs.getString("values")), + parseMax(rs.getString("values")), rs.getString("studyAcronym"), null ); } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptService.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptService.java index 67db7b6..f22b65e 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptService.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptService.java @@ -49,16 +49,14 @@ public Optional conceptDetail(String dataset, String conceptPath) { } private Optional getConcept(String dataset, String conceptPath, boolean addAncestors) { - Optional concept = conceptRepository.getConcept(dataset, conceptPath) - .map(core -> { - var meta = conceptRepository.getConceptMeta(dataset, conceptPath); - return switch (core) { - case ContinuousConcept cont -> new ContinuousConcept(cont, meta); - case CategoricalConcept cat -> new CategoricalConcept(cat, meta); - case ConceptShell ignored -> throw new RuntimeException("Concept shell escaped to API"); - }; - } - ); + Optional concept = conceptRepository.getConcept(dataset, conceptPath).map(core -> { + var meta = conceptRepository.getConceptMeta(dataset, conceptPath); + return switch (core) { + case ContinuousConcept cont -> new ContinuousConcept(cont, meta); + case CategoricalConcept cat -> new CategoricalConcept(cat, meta); + case ConceptShell ignored -> throw new RuntimeException("Concept shell escaped to API"); + }; + }); return addAncestors ? concept.map(conceptDecoratorService::populateParentConcepts) : concept; } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptType.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptType.java index e0ec6d1..33f9f39 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptType.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptType.java @@ -9,8 +9,7 @@ public enum ConceptType { Categorical, /** - * i.e. Age: 0 - 150 - * Also known as numeric (to me) + * i.e. Age: 0 - 150 Also known as numeric (to me) */ Continuous; diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardConfig.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardConfig.java index 024e9b3..0dfb5b8 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardConfig.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardConfig.java @@ -24,10 +24,8 @@ public DashboardConfig( @Bean public List getColumns() { - return labelDisplayElementPairs.entrySet().stream() - .map(e -> new DashboardColumn(e.getKey(), e.getValue())) - .sorted((a, b) -> Integer.compare(calculateOrder(a), calculateOrder(b))) - .toList(); + return labelDisplayElementPairs.entrySet().stream().map(e -> new DashboardColumn(e.getKey(), e.getValue())) + .sorted((a, b) -> Integer.compare(calculateOrder(a), calculateOrder(b))).toList(); } private int calculateOrder(DashboardColumn column) { diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepository.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepository.java index 6d205da..2a0dd7f 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepository.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepository.java @@ -22,10 +22,8 @@ public class DashboardRepository { @Autowired public DashboardRepository( - NamedParameterJdbcTemplate template, - List columns, - @Value("${dashboard.nonmeta-columns}") - Set nonMetaColumns, DashboardRowResultSetExtractor extractor + NamedParameterJdbcTemplate template, List columns, + @Value("${dashboard.nonmeta-columns}") Set nonMetaColumns, DashboardRowResultSetExtractor extractor ) { this.template = template; this.columns = columns; @@ -33,7 +31,7 @@ public DashboardRepository( this.extractor = extractor; } - private static final class ListMapExtractor implements ResultSetExtractor>> { + private static final class ListMapExtractor implements ResultSetExtractor>> { @Override public List> extractData(ResultSet rs) throws SQLException, DataAccessException { @@ -51,41 +49,42 @@ public List> extractData(ResultSet rs) throws SQLException, } public List> getHackyBDCRows() { - String sql = """ - SELECT - dataset.abbreviation AS abbreviation, - dataset.full_name AS name, - CASE - WHEN consent.variable_count > -1 THEN consent.variable_count::text - ELSE 'N/A' - END - AS clinvars, - CASE - WHEN consent.participant_count > -1 THEN consent.participant_count::text - ELSE 'N/A' - END - AS participants, - CASE - WHEN consent.sample_count > -1 THEN consent.sample_count::text - ELSE 'N/A' - END - AS samples, - CASE - WHEN (consent.consent_code IS NOT NULL AND consent.consent_code != '') THEN concat(study_accession_meta.value, '.', consent.consent_code) - ELSE study_accession_meta.value - END - AS accession, - study_focus_meta.value AS study_focus, - additional_info_meta.value AS additional_info_link - FROM - dataset - LEFT JOIN consent ON consent.dataset_id = dataset.dataset_id - LEFT JOIN dataset_meta AS study_focus_meta ON study_focus_meta.dataset_id = dataset.dataset_id AND study_focus_meta.KEY = 'study_focus' - LEFT JOIN dataset_meta AS study_accession_meta ON study_accession_meta.dataset_id = dataset.dataset_id AND study_accession_meta.KEY = 'study_accession' - LEFT JOIN dataset_meta AS additional_info_meta ON additional_info_meta.dataset_id = dataset.dataset_id AND additional_info_meta.KEY = 'study_link' - WHERE dataset.dataset_id NOT IN (select dataset_id from dataset_meta where KEY = 'show_dashboad' and VALUE = 'false') - ORDER BY name ASC, abbreviation ASC - """; + String sql = + """ + SELECT + dataset.abbreviation AS abbreviation, + dataset.full_name AS name, + CASE + WHEN consent.variable_count > -1 THEN consent.variable_count::text + ELSE 'N/A' + END + AS clinvars, + CASE + WHEN consent.participant_count > -1 THEN consent.participant_count::text + ELSE 'N/A' + END + AS participants, + CASE + WHEN consent.sample_count > -1 THEN consent.sample_count::text + ELSE 'N/A' + END + AS samples, + CASE + WHEN (consent.consent_code IS NOT NULL AND consent.consent_code != '') THEN concat(study_accession_meta.value, '.', consent.consent_code) + ELSE study_accession_meta.value + END + AS accession, + study_focus_meta.value AS study_focus, + additional_info_meta.value AS additional_info_link + FROM + dataset + LEFT JOIN consent ON consent.dataset_id = dataset.dataset_id + LEFT JOIN dataset_meta AS study_focus_meta ON study_focus_meta.dataset_id = dataset.dataset_id AND study_focus_meta.KEY = 'study_focus' + LEFT JOIN dataset_meta AS study_accession_meta ON study_accession_meta.dataset_id = dataset.dataset_id AND study_accession_meta.KEY = 'study_accession' + LEFT JOIN dataset_meta AS additional_info_meta ON additional_info_meta.dataset_id = dataset.dataset_id AND additional_info_meta.KEY = 'study_link' + WHERE dataset.dataset_id NOT IN (select dataset_id from dataset_meta where KEY = 'show_dashboad' and VALUE = 'false') + ORDER BY name ASC, abbreviation ASC + """; return template.query(sql, new ListMapExtractor()); } @@ -102,10 +101,7 @@ public List> getRows() { dataset_meta.KEY IN (:keys) ORDER BY name ASC, abbreviation ASC """; - List keys = columns.stream() - .map(DashboardColumn::dataElement) - .filter(Predicate.not(nonMetaColumns::contains)) - .toList(); + List keys = columns.stream().map(DashboardColumn::dataElement).filter(Predicate.not(nonMetaColumns::contains)).toList(); MapSqlParameterSource params = new MapSqlParameterSource().addValue("keys", keys); return template.query(sql, params, extractor); } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRowResultSetExtractor.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRowResultSetExtractor.java index 7198063..be374f0 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRowResultSetExtractor.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRowResultSetExtractor.java @@ -22,8 +22,7 @@ public class DashboardRowResultSetExtractor implements ResultSetExtractor columns) { - template = columns.stream() - .collect(Collectors.toMap(DashboardColumn::dataElement, (ignored) -> "")); + template = columns.stream().collect(Collectors.toMap(DashboardColumn::dataElement, (ignored) -> "")); } @Override diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardService.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardService.java index 2364bff..be16f9a 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardService.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardService.java @@ -16,9 +16,7 @@ public class DashboardService { @Autowired public DashboardService( - DashboardRepository repository, - List columns, - @Value("${dashboard.enable.bdc_hack}") boolean bdcHack + DashboardRepository repository, List columns, @Value("${dashboard.enable.bdc_hack}") boolean bdcHack ) { this.repository = repository; this.columns = columns; @@ -28,26 +26,16 @@ public DashboardService( public Dashboard getDashboard() { if (bdcHack) { List> rows = repository.getHackyBDCRows(); - return new Dashboard( - hackyBDCColumns, - rows - ); + return new Dashboard(hackyBDCColumns, rows); } List> rows = repository.getRows(); - return new Dashboard( - columns, - rows - ); + return new Dashboard(columns, rows); } private static final List hackyBDCColumns = List.of( - new DashboardColumn("abbreviation", "Abbreviation"), - new DashboardColumn("name", "Name"), - new DashboardColumn("study_focus", "Study Focus"), - new DashboardColumn("clinvars", "Clinical Variables"), - new DashboardColumn("participants", "Participants"), - new DashboardColumn("samples", "Samples Sequenced"), - new DashboardColumn("accession", "Accession"), - new DashboardColumn("additional_info_link", "Study Link") + new DashboardColumn("abbreviation", "Abbreviation"), new DashboardColumn("name", "Name"), + new DashboardColumn("study_focus", "Study Focus"), new DashboardColumn("clinvars", "Clinical Variables"), + new DashboardColumn("participants", "Participants"), new DashboardColumn("samples", "Samples Sequenced"), + new DashboardColumn("accession", "Accession"), new DashboardColumn("additional_info_link", "Study Link") ); } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/Facet.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/Facet.java index 58dc1a9..6c93a58 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/Facet.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/Facet.java @@ -6,9 +6,8 @@ import java.util.Map; public record Facet( - String name, String display, String description, String fullName, - @Nullable Integer count, @Nullable List children, String category, - @Nullable Map meta + String name, String display, String description, String fullName, @Nullable Integer count, @Nullable List children, + String category, @Nullable Map meta ) { public Facet(Facet core, Map meta) { this(core.name(), core.display(), core.description(), core.fullName(), core.count(), core.children(), core.category(), meta); diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetCategory.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetCategory.java index aee9fb1..d775ee6 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetCategory.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetCategory.java @@ -2,10 +2,7 @@ import java.util.List; -public record FacetCategory( - String name, String display, String description, - List facets -) { +public record FacetCategory(String name, String display, String description, List facets) { public FacetCategory(FacetCategory core, List facets) { this(core.name(), core.display(), core.description(), facets); } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetCategoryExtractor.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetCategoryExtractor.java index 8f6d3c1..5c13251 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetCategoryExtractor.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetCategoryExtractor.java @@ -27,14 +27,11 @@ public List extractData(ResultSet rs) throws SQLException, DataAc // build out all the facets and make shells of the facet categories String category = rs.getString("category_name"); Facet facet = new Facet( - rs.getString("name"), rs.getString("display"), - rs.getString("description"), rs.getString("full_name"), rs.getInt("facet_count"), - List.of(), category, null - ); - FacetCategory facetCategory = new FacetCategory( - category, rs.getString("category_display"), - rs.getString("category_description"), List.of() + rs.getString("name"), rs.getString("display"), rs.getString("description"), rs.getString("full_name"), + rs.getInt("facet_count"), List.of(), category, null ); + FacetCategory facetCategory = + new FacetCategory(category, rs.getString("category_display"), rs.getString("category_description"), List.of()); String parentName = rs.getString("parent_name"); if (StringUtils.hasLength(parentName)) { Pair key = new Pair(parentName, category); @@ -46,16 +43,15 @@ public List extractData(ResultSet rs) throws SQLException, DataAc } categories.put(category, facetCategory); } - facets = facets.stream() - .map(f -> f.withChildren(childrenForParent.getOrDefault(new Pair(f), List.of()))) - .toList(); + facets = facets.stream().map(f -> f.withChildren(childrenForParent.getOrDefault(new Pair(f), List.of()))).toList(); // group facets by category, then add them to their respective category Map> grouped = facets.stream().collect(Collectors.groupingBy(Facet::category)); return categories.entrySet().stream() - .map(e -> new FacetCategory( - e.getValue(), - grouped.getOrDefault(e.getKey(), List.of()).stream().sorted(Comparator.comparingInt(Facet::count).reversed()).toList() - )) - .toList(); + .map( + e -> new FacetCategory( + e.getValue(), + grouped.getOrDefault(e.getKey(), List.of()).stream().sorted(Comparator.comparingInt(Facet::count).reversed()).toList() + ) + ).toList(); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetController.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetController.java index 87d3147..a3d9a1c 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetController.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetController.java @@ -25,12 +25,7 @@ public ResponseEntity> getFacets(@RequestBody Filter filter) } @GetMapping(path = "/facets/{facetCategory}/{facet}") - public ResponseEntity facetDetails( - @PathVariable String facetCategory, - @PathVariable String facet - ) { - return facetService.facetDetails(facetCategory, facet) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); + public ResponseEntity facetDetails(@PathVariable String facetCategory, @PathVariable String facet) { + return facetService.facetDetails(facetCategory, facet).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build()); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetMapper.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetMapper.java index c74f988..299e121 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetMapper.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetMapper.java @@ -12,14 +12,8 @@ public class FacetMapper implements RowMapper { @Override public Facet mapRow(ResultSet rs, int rowNum) throws SQLException { return new Facet( - rs.getString("name"), - rs.getString("display"), - rs.getString("description"), - rs.getString("full_name"), - null, - List.of(), - rs.getString("category"), - null + rs.getString("name"), rs.getString("display"), rs.getString("description"), rs.getString("full_name"), null, List.of(), + rs.getString("category"), null ); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetQueryGenerator.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetQueryGenerator.java index 81eb2a0..c56fbd1 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetQueryGenerator.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetQueryGenerator.java @@ -16,29 +16,29 @@ public class FacetQueryGenerator { private static final String CONSENT_QUERY = """ - dataset.dataset_id IN ( - SELECT - consent.dataset_id - FROM consent - LEFT JOIN dataset ON dataset.dataset_id = consent.dataset_id - WHERE - concat(dataset.ref, '.', consent.consent_code) IN (:consents) OR - (dataset.ref IN (:consents) AND consent.consent_code = '') - UNION - SELECT - dataset_harmonization.harmonized_dataset_id - FROM consent - JOIN dataset_harmonization ON dataset_harmonization.source_dataset_id = consent.dataset_id - LEFT JOIN dataset ON dataset.dataset_id = dataset_harmonization.source_dataset_id - WHERE - concat(dataset.ref, '.', consent.consent_code) IN (:consents) OR - (dataset.ref IN (:consents) AND consent.consent_code = '') - ) AND - """; + dataset.dataset_id IN ( + SELECT + consent.dataset_id + FROM consent + LEFT JOIN dataset ON dataset.dataset_id = consent.dataset_id + WHERE + concat(dataset.ref, '.', consent.consent_code) IN (:consents) OR + (dataset.ref IN (:consents) AND consent.consent_code = '') + UNION + SELECT + dataset_harmonization.harmonized_dataset_id + FROM consent + JOIN dataset_harmonization ON dataset_harmonization.source_dataset_id = consent.dataset_id + LEFT JOIN dataset ON dataset.dataset_id = dataset_harmonization.source_dataset_id + WHERE + concat(dataset.ref, '.', consent.consent_code) IN (:consents) OR + (dataset.ref IN (:consents) AND consent.consent_code = '') + ) AND + """; public String createFacetSQLAndPopulateParams(Filter filter, MapSqlParameterSource params) { - Map> groupedFacets = (filter.facets() == null ? Stream.of() : filter.facets().stream()) - .collect(Collectors.groupingBy(Facet::category)); + Map> groupedFacets = + (filter.facets() == null ? Stream.of() : filter.facets().stream()).collect(Collectors.groupingBy(Facet::category)); String consentWhere = ""; if (!CollectionUtils.isEmpty(filter.consents())) { params.addValue("consents", filter.consents()); @@ -73,20 +73,19 @@ private Map createSQLSafeCategoryKeys(List categories) { return keys; } - private String createMultiCategorySQLWithSearch(Map> facets, String search, String consentWhere, MapSqlParameterSource params) { + private String createMultiCategorySQLWithSearch( + Map> facets, String search, String consentWhere, MapSqlParameterSource params + ) { Map categoryKeys = createSQLSafeCategoryKeys(facets.keySet().stream().toList()); params.addValue("search", search); /* - For each category of facet present in the filter, create a query that represents all the concept IDs - associated with the selected facets in that category - */ + * For each category of facet present in the filter, create a query that represents all the concept IDs associated with the selected + * facets in that category + */ String conceptsQuery = "WITH " + facets.keySet().stream().map(category -> { - List selectedFacetsInCateory = facets.entrySet().stream() - .filter(e -> category.equals(e.getKey())) - .flatMap(e -> e.getValue().stream()) - .map(facet -> new String[]{facet.category(), facet.name()}) - .toList(); + List selectedFacetsInCateory = facets.entrySet().stream().filter(e -> category.equals(e.getKey())) + .flatMap(e -> e.getValue().stream()).map(facet -> new String[] {facet.category(), facet.name()}).toList(); params.addValue("facets_in_cat_" + categoryKeys.get(category), selectedFacetsInCateory); params.addValue("facet_category_" + categoryKeys.get(category), category); return """ @@ -110,49 +109,45 @@ private String createMultiCategorySQLWithSearch(Map> facets, categorical_values.value <> '' ) ) - """.formatted(categoryKeys.get(category), categoryKeys.get(category)); + """ + .formatted(categoryKeys.get(category), categoryKeys.get(category)); }).collect(Collectors.joining(",\n")); /* - Categories with no selected facets contribute no concepts, so ignore them for now. - Now, for each category with selected facets, take all the concepts from all other categories with selections - and INTERSECT them. This creates the concepts for this category + * Categories with no selected facets contribute no concepts, so ignore them for now. Now, for each category with selected facets, + * take all the concepts from all other categories with selections and INTERSECT them. This creates the concepts for this category */ String selectedFacetsQuery = facets.keySet().stream().map(category -> { - String allConceptsForCategory = categoryKeys.values().stream() - .filter(key -> !categoryKeys.get(category).equals(key)) - .map(key -> "SELECT * FROM facet_category_" + key + "_concepts") - .collect(Collectors.joining(" INTERSECT ")); - params.addValue("", ""); - return """ - ( - SELECT - facet.facet_id, count(*) as facet_count - FROM - facet - LEFT JOIN facet_category fc ON fc.facet_category_id = facet.facet_category_id - JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id - LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id - LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id - WHERE - %s - fcn.concept_node_id IN (%s) AND - fc.name = :facet_category_%s - GROUP BY - facet.facet_id - ORDER BY - facet_count DESC - ) - """.formatted(consentWhere, allConceptsForCategory, categoryKeys.get(category)); - }) - .collect(Collectors.joining("\n\tUNION\n")); + String allConceptsForCategory = categoryKeys.values().stream().filter(key -> !categoryKeys.get(category).equals(key)) + .map(key -> "SELECT * FROM facet_category_" + key + "_concepts").collect(Collectors.joining(" INTERSECT ")); + params.addValue("", ""); + return """ + ( + SELECT + facet.facet_id, count(*) as facet_count + FROM + facet + LEFT JOIN facet_category fc ON fc.facet_category_id = facet.facet_category_id + JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id + LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id + LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + WHERE + %s + fcn.concept_node_id IN (%s) AND + fc.name = :facet_category_%s + GROUP BY + facet.facet_id + ORDER BY + facet_count DESC + ) + """.formatted(consentWhere, allConceptsForCategory, categoryKeys.get(category)); + }).collect(Collectors.joining("\n\tUNION\n")); /* - For categories with no selected facets, take all the concepts from all facets, and use them for the counts + * For categories with no selected facets, take all the concepts from all facets, and use them for the counts */ params.addValue("all_selected_facet_categories", facets.keySet()); String allConceptsForUnselectedCategories = categoryKeys.values().stream() - .map(key -> "SELECT * FROM facet_category_" + key + "_concepts") - .collect(Collectors.joining(" INTERSECT ")); + .map(key -> "SELECT * FROM facet_category_" + key + "_concepts").collect(Collectors.joining(" INTERSECT ")); String unselectedFacetsQuery = """ UNION ( @@ -182,15 +177,12 @@ private String createMultiCategorySQLNoSearch(Map> facets, S Map categoryKeys = createSQLSafeCategoryKeys(facets.keySet().stream().toList()); /* - For each category of facet present in the filter, create a query that represents all the concept IDs - associated with the selected facets in that category - */ + * For each category of facet present in the filter, create a query that represents all the concept IDs associated with the selected + * facets in that category + */ String conceptsQuery = "WITH " + facets.keySet().stream().map(category -> { - List selectedFacetsInCateory = facets.entrySet().stream() - .filter(e -> category.equals(e.getKey())) - .flatMap(e -> e.getValue().stream()) - .map(facet -> new String[]{facet.category(), facet.name()}) - .toList(); + List selectedFacetsInCateory = facets.entrySet().stream().filter(e -> category.equals(e.getKey())) + .flatMap(e -> e.getValue().stream()).map(facet -> new String[] {facet.category(), facet.name()}).toList(); params.addValue("facets_in_cat_" + categoryKeys.get(category), selectedFacetsInCateory); params.addValue("facet_category_" + categoryKeys.get(category), category); return """ @@ -213,20 +205,19 @@ private String createMultiCategorySQLNoSearch(Map> facets, S categorical_values.value <> '' ) ) - """.formatted(categoryKeys.get(category), categoryKeys.get(category)); + """ + .formatted(categoryKeys.get(category), categoryKeys.get(category)); }).collect(Collectors.joining(",\n")); /* - Now, for each category with selected facets, take all the concepts from all other categories with selections - and INTERSECT them. This creates the concepts for this category + * Now, for each category with selected facets, take all the concepts from all other categories with selections and INTERSECT them. + * This creates the concepts for this category */ String selectedFacetsQuery = facets.keySet().stream().map(category -> { - params.addValue("facet_category_" + categoryKeys.get(category), category); - String allConceptsForCategory = categoryKeys.values().stream() - .filter(key -> !categoryKeys.get(category).equals(key)) - .map(key -> "SELECT * FROM facet_category_" + key + "_concepts") - .collect(Collectors.joining(" INTERSECT ")); - params.addValue("", ""); - return """ + params.addValue("facet_category_" + categoryKeys.get(category), category); + String allConceptsForCategory = categoryKeys.values().stream().filter(key -> !categoryKeys.get(category).equals(key)) + .map(key -> "SELECT * FROM facet_category_" + key + "_concepts").collect(Collectors.joining(" INTERSECT ")); + params.addValue("", ""); + return """ ( SELECT facet.facet_id, count(*) as facet_count @@ -246,16 +237,14 @@ fcn.concept_node_id IN (%s) facet_count DESC ) """.formatted(consentWhere, allConceptsForCategory, categoryKeys.get(category)); - }) - .collect(Collectors.joining("\n\tUNION\n")); + }).collect(Collectors.joining("\n\tUNION\n")); /* - For categories with no selected facets, take all the concepts from all facets, and use them for the counts + * For categories with no selected facets, take all the concepts from all facets, and use them for the counts */ params.addValue("all_selected_facet_categories", facets.keySet()); String allConceptsForUnselectedCategories = categoryKeys.values().stream() - .map(key -> "SELECT * FROM facet_category_" + key + "_concepts") - .collect(Collectors.joining(" INTERSECT ")); + .map(key -> "SELECT * FROM facet_category_" + key + "_concepts").collect(Collectors.joining(" INTERSECT ")); String unselectedFacetsQuery = """ UNION ( @@ -286,81 +275,82 @@ private String createSingleCategorySQLWithSearch(List facets, String sear params.addValue("facets", facets.stream().map(Facet::name).toList()); params.addValue("search", search); // return all the facets that - // are in the matched category - // are displayable - // match a concept with search hits + // are in the matched category + // are displayable + // match a concept with search hits // UNION // all the facets from other categories that match concepts that - // match selected facets from this category - // match search + // match selected facets from this category + // match search return """ - ( + ( + SELECT + facet.facet_id, count(*) as facet_count + FROM + facet + JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id + JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id + LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id + LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' + LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' + LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' + WHERE + %s + fc.name = :facet_category_name + AND concept_node.searchable_fields @@ (phraseto_tsquery(:search)::text || ':*')::tsquery + AND ( + continuous_min.value <> '' OR + continuous_max.value <> '' OR + categorical_values.value <> '' + ) + GROUP BY + facet.facet_id + ORDER BY + facet_count DESC + ) + UNION + ( + WITH matching_concepts AS ( SELECT - facet.facet_id, count(*) as facet_count + DISTINCT(concept_node.concept_node_id) AS concept_node_id FROM facet - JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id - LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id - LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id + JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' WHERE - %s fc.name = :facet_category_name + AND facet.name IN (:facets) AND concept_node.searchable_fields @@ (phraseto_tsquery(:search)::text || ':*')::tsquery AND ( continuous_min.value <> '' OR continuous_max.value <> '' OR categorical_values.value <> '' ) - GROUP BY - facet.facet_id - ORDER BY - facet_count DESC ) - UNION - ( - WITH matching_concepts AS ( - SELECT - DISTINCT(concept_node.concept_node_id) AS concept_node_id - FROM - facet - JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id - JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id - JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id - LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' - LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' - LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' - WHERE - fc.name = :facet_category_name - AND facet.name IN (:facets) - AND concept_node.searchable_fields @@ (phraseto_tsquery(:search)::text || ':*')::tsquery - AND ( - continuous_min.value <> '' OR - continuous_max.value <> '' OR - categorical_values.value <> '' - ) - ) - SELECT - facet.facet_id, count(*) as facet_count - FROM - facet - JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id - LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id - LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id - JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id - JOIN matching_concepts ON fcn.concept_node_id = matching_concepts.concept_node_id - WHERE - %s - fc.name <> :facet_category_name - GROUP BY - facet.facet_id - ORDER BY - facet_count DESC - ) - """.formatted(consentWhere, consentWhere); + SELECT + facet.facet_id, count(*) as facet_count + FROM + facet + JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id + LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id + LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id + JOIN matching_concepts ON fcn.concept_node_id = matching_concepts.concept_node_id + WHERE + %s + fc.name <> :facet_category_name + GROUP BY + facet.facet_id + ORDER BY + facet_count DESC + ) + """ + .formatted(consentWhere, consentWhere); } private String createSingleCategorySQLNoSearch(List facets, String consentWhere, MapSqlParameterSource params) { @@ -370,77 +360,78 @@ private String createSingleCategorySQLNoSearch(List facets, String consen // UNION // all the facets from other categories that match concepts that match selected facets from this category return """ - ( + ( + SELECT + facet.facet_id, count(*) as facet_count + FROM + facet + JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id + JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id + LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id + LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' + LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' + LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' + WHERE + %s + fc.name = :facet_category_name + AND ( + continuous_min.value <> '' OR + continuous_max.value <> '' OR + categorical_values.value <> '' + ) + GROUP BY + facet.facet_id + ORDER BY + facet_count DESC + ) + UNION + ( + WITH matching_concepts AS ( SELECT - facet.facet_id, count(*) as facet_count + DISTINCT(concept_node.concept_node_id) AS concept_node_id FROM facet JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id - LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id - LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' WHERE - %s fc.name = :facet_category_name + AND facet.name IN (:facets) AND ( continuous_min.value <> '' OR continuous_max.value <> '' OR categorical_values.value <> '' ) - GROUP BY - facet.facet_id - ORDER BY - facet_count DESC - ) - UNION - ( - WITH matching_concepts AS ( - SELECT - DISTINCT(concept_node.concept_node_id) AS concept_node_id - FROM - facet - JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id - JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id - JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id - LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min' - LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max' - LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values' - WHERE - fc.name = :facet_category_name - AND facet.name IN (:facets) - AND ( - continuous_min.value <> '' OR - continuous_max.value <> '' OR - categorical_values.value <> '' - ) - ) - SELECT - facet.facet_id, count(*) as facet_count - FROM - facet - JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id - LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id - LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id - JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id - JOIN matching_concepts ON fcn.concept_node_id = matching_concepts.concept_node_id - WHERE - %s - fc.name <> :facet_category_name - GROUP BY - facet.facet_id - ORDER BY - facet_count DESC ) - """.formatted(consentWhere, consentWhere); + SELECT + facet.facet_id, count(*) as facet_count + FROM + facet + JOIN facet__concept_node fcn ON fcn.facet_id = facet.facet_id + LEFT JOIN concept_node ON concept_node.concept_node_id = fcn.concept_node_id + LEFT JOIN dataset ON concept_node.dataset_id = dataset.dataset_id + JOIN facet_category fc on fc.facet_category_id = facet.facet_category_id + JOIN matching_concepts ON fcn.concept_node_id = matching_concepts.concept_node_id + WHERE + %s + fc.name <> :facet_category_name + GROUP BY + facet.facet_id + ORDER BY + facet_count DESC + ) + """ + .formatted(consentWhere, consentWhere); } private String createNoFacetSQLWithSearch(String search, String consentWhere, MapSqlParameterSource params) { // return all the facets that match concepts that - // match search - // are displayable + // match search + // are displayable params.addValue("search", search); return """ SELECT @@ -466,7 +457,8 @@ facet.facet_id, count(*) as facet_count facet.facet_id ORDER BY facet_count DESC - """.formatted(consentWhere); + """ + .formatted(consentWhere); } @@ -496,6 +488,7 @@ facet.facet_id, count(*) as facet_count facet.facet_id ORDER BY facet_count DESC - """.formatted(consents); + """ + .formatted(consents); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepository.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepository.java index 1cfd49d..40ba723 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepository.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepository.java @@ -21,9 +21,7 @@ public class FacetRepository { private final FacetQueryGenerator generator; @Autowired - public FacetRepository( - NamedParameterJdbcTemplate template, FacetQueryGenerator generator, FacetMapper mapper - ) { + public FacetRepository(NamedParameterJdbcTemplate template, FacetQueryGenerator generator, FacetMapper mapper) { this.template = template; this.generator = generator; this.mapper = mapper; @@ -33,69 +31,69 @@ public List getFacets(Filter filter) { MapSqlParameterSource params = new MapSqlParameterSource(); String innerSQL = generator.createFacetSQLAndPopulateParams(filter, params); // return a list of facets and the number of concepts associated with them - String sql = """ - WITH facet_counts_q AS ( - %s - ) - SELECT - facet_category.name AS category_name, - parent_facet.name AS parent_name, - facet_counts_q.facet_count AS facet_count, - facet_category.display as category_display, - facet_category.description as category_description, - facet.name, facet.display, facet.description, - facet_meta_full_name.value AS full_name - FROM - facet - LEFT JOIN facet_counts_q ON facet.facet_id = facet_counts_q.facet_id - LEFT JOIN facet_category ON facet_category.facet_category_id = facet.facet_category_id - LEFT JOIN facet as parent_facet ON facet.parent_id = parent_facet.facet_id - LEFT JOIN facet_meta AS facet_meta_full_name ON facet.facet_id = facet_meta_full_name.facet_id AND facet_meta_full_name.KEY = 'full_name' - - """.formatted(innerSQL); + String sql = + """ + WITH facet_counts_q AS ( + %s + ) + SELECT + facet_category.name AS category_name, + parent_facet.name AS parent_name, + facet_counts_q.facet_count AS facet_count, + facet_category.display as category_display, + facet_category.description as category_description, + facet.name, facet.display, facet.description, + facet_meta_full_name.value AS full_name + FROM + facet + LEFT JOIN facet_counts_q ON facet.facet_id = facet_counts_q.facet_id + LEFT JOIN facet_category ON facet_category.facet_category_id = facet.facet_category_id + LEFT JOIN facet as parent_facet ON facet.parent_id = parent_facet.facet_id + LEFT JOIN facet_meta AS facet_meta_full_name ON facet.facet_id = facet_meta_full_name.facet_id AND facet_meta_full_name.KEY = 'full_name' + + """ + .formatted(innerSQL); return template.query(sql, params, new FacetCategoryExtractor()); } public Optional getFacet(String facetCategory, String facet) { - String sql = """ - SELECT - facet_category.name AS category, - facet.name, facet.display, facet.description, - facet_meta_full_name.value AS full_name - FROM - facet - LEFT JOIN facet_category ON facet_category.facet_category_id = facet.facet_category_id - LEFT JOIN facet_meta AS facet_meta_full_name ON facet.facet_id = facet_meta_full_name.facet_id AND facet_meta_full_name.KEY = 'full_name' - WHERE - facet.name = :facetName - AND facet_category.name = :facetCategory - """; - MapSqlParameterSource params = new MapSqlParameterSource() - .addValue("facetCategory", facetCategory) - .addValue("facetName", facet); - return template.query(sql, params, mapper).stream().findFirst() - .map(f -> f.withChildren(getFacetChildren(f.category(), f.name()))); + String sql = + """ + SELECT + facet_category.name AS category, + facet.name, facet.display, facet.description, + facet_meta_full_name.value AS full_name + FROM + facet + LEFT JOIN facet_category ON facet_category.facet_category_id = facet.facet_category_id + LEFT JOIN facet_meta AS facet_meta_full_name ON facet.facet_id = facet_meta_full_name.facet_id AND facet_meta_full_name.KEY = 'full_name' + WHERE + facet.name = :facetName + AND facet_category.name = :facetCategory + """; + MapSqlParameterSource params = new MapSqlParameterSource().addValue("facetCategory", facetCategory).addValue("facetName", facet); + return template.query(sql, params, mapper).stream().findFirst().map(f -> f.withChildren(getFacetChildren(f.category(), f.name()))); } private List getFacetChildren(String facetCategory, String parentFacetName) { - String sql = """ - SELECT - facet_category.name AS category, - facet.name, facet.display, facet.description, - facet_meta_full_name.value AS full_name - FROM - facet - LEFT JOIN facet as parent_facet ON facet.parent_id = parent_facet.facet_id - LEFT JOIN facet_category ON facet_category.facet_category_id = facet.facet_category_id - LEFT JOIN facet_meta AS facet_meta_full_name ON facet.facet_id = facet_meta_full_name.facet_id AND facet_meta_full_name.KEY = 'full_name' - WHERE - parent_facet.name = :facetName - AND facet_category.name = :facetCategory - """; - MapSqlParameterSource params = new MapSqlParameterSource() - .addValue("facetCategory", facetCategory) - .addValue("facetName", parentFacetName); + String sql = + """ + SELECT + facet_category.name AS category, + facet.name, facet.display, facet.description, + facet_meta_full_name.value AS full_name + FROM + facet + LEFT JOIN facet as parent_facet ON facet.parent_id = parent_facet.facet_id + LEFT JOIN facet_category ON facet_category.facet_category_id = facet.facet_category_id + LEFT JOIN facet_meta AS facet_meta_full_name ON facet.facet_id = facet_meta_full_name.facet_id AND facet_meta_full_name.KEY = 'full_name' + WHERE + parent_facet.name = :facetName + AND facet_category.name = :facetCategory + """; + MapSqlParameterSource params = + new MapSqlParameterSource().addValue("facetCategory", facetCategory).addValue("facetName", parentFacetName); return template.query(sql, params, mapper); } @@ -111,9 +109,7 @@ public Map getFacetMeta(String facetCategory, String facet) { facet.name = :facetName AND facet_category.name = :facetCategory """; - MapSqlParameterSource params = new MapSqlParameterSource() - .addValue("facetCategory", facetCategory) - .addValue("facetName", facet); + MapSqlParameterSource params = new MapSqlParameterSource().addValue("facetCategory", facetCategory).addValue("facetName", facet); return template.query(sql, params, new MapExtractor("KEY", "VALUE")); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetService.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetService.java index 58bc779..b330865 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetService.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetService.java @@ -22,7 +22,6 @@ public List getFacets(Filter filter) { } public Optional facetDetails(String facetCategory, String facet) { - return repository.getFacet(facetCategory, facet) - .map(f -> new Facet(f, repository.getFacetMeta(facetCategory, facet))); + return repository.getFacet(facetCategory, facet).map(f -> new Facet(f, repository.getFacetMeta(facetCategory, facet))); } } diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FilterPreProcessor.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FilterPreProcessor.java index 594407a..483dc02 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FilterPreProcessor.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FilterPreProcessor.java @@ -14,24 +14,21 @@ @ControllerAdvice public class FilterPreProcessor implements RequestBodyAdvice { @Override - public boolean supports( - MethodParameter methodParameter, Type targetType, Class> converterType - ) { + public boolean supports(MethodParameter methodParameter, Type targetType, Class> converterType) { return true; } @Override public HttpInputMessage beforeBodyRead( - HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, - Class> converterType + HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class> converterType ) throws IOException { return inputMessage; } @Override public Object afterBodyRead( - Object body, HttpInputMessage inputMessage, MethodParameter parameter, - Type targetType, Class> converterType + Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, + Class> converterType ) { if (body instanceof Filter filter && StringUtils.hasLength(filter.search())) { return new Filter(filter.facets(), filter.search().replaceAll("_", "/"), filter.consents()); diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/filter/QueryParamPair.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/filter/QueryParamPair.java index bf3390c..ae26e48 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/filter/QueryParamPair.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/filter/QueryParamPair.java @@ -2,4 +2,5 @@ import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -public record QueryParamPair(String query, MapSqlParameterSource params) {} +public record QueryParamPair(String query, MapSqlParameterSource params) { +} diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/info/InfoResponse.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/info/InfoResponse.java index 1e26313..5717fbf 100644 --- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/info/InfoResponse.java +++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/info/InfoResponse.java @@ -4,4 +4,4 @@ import java.util.UUID; public record InfoResponse(UUID id, String name, List queryFormats) { -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/DictionaryApplicationTests.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/DictionaryApplicationTests.java index 4ffbb21..58c4c1f 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/DictionaryApplicationTests.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/DictionaryApplicationTests.java @@ -6,8 +6,7 @@ @SpringBootTest class DictionaryApplicationTests { - @Test - void contextLoads() { - } + @Test + void contextLoads() {} } diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/TestDictionaryApplication.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/TestDictionaryApplication.java index f8fe770..6cc5fe4 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/TestDictionaryApplication.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/TestDictionaryApplication.java @@ -10,14 +10,14 @@ @TestConfiguration(proxyBeanMethods = false) public class TestDictionaryApplication { - @Bean - @ServiceConnection - PostgreSQLContainer postgresContainer() { - return new PostgreSQLContainer<>(DockerImageName.parse("postgres:latest")); - } + @Bean + @ServiceConnection + PostgreSQLContainer postgresContainer() { + return new PostgreSQLContainer<>(DockerImageName.parse("postgres:latest")); + } - public static void main(String[] args) { - SpringApplication.from(DictionaryApplication::main).with(TestDictionaryApplication.class).run(args); - } + public static void main(String[] args) { + SpringApplication.from(DictionaryApplication::main).with(TestDictionaryApplication.class).run(args); + } } diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptControllerTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptControllerTest.java index 5452441..a5d5fe6 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptControllerTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptControllerTest.java @@ -37,14 +37,10 @@ void shouldListConcepts() { new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), true, "", List.of(), Map.of()), new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()) ); - Filter filter = new Filter( - List.of(new Facet("questionare", "Questionare", "?", "Questionare", 1, null, "category", null)), - "foo", List.of() - ); - Mockito.when(conceptService.listConcepts(filter, Pageable.ofSize(10).withPage(1))) - .thenReturn(expected); - Mockito.when(conceptService.countConcepts(filter)) - .thenReturn(100L); + Filter filter = + new Filter(List.of(new Facet("questionare", "Questionare", "?", "Questionare", 1, null, "category", null)), "foo", List.of()); + Mockito.when(conceptService.listConcepts(filter, Pageable.ofSize(10).withPage(1))).thenReturn(expected); + Mockito.when(conceptService.countConcepts(filter)).thenReturn(100L); Page actual = subject.listConcepts(filter, 1, 10).getBody(); @@ -56,8 +52,7 @@ void shouldListConcepts() { void shouldGetConceptDetails() { CategoricalConcept expected = new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), true, "", List.of(), Map.of()); - Mockito.when(conceptService.conceptDetail("my_dataset", "/foo//bar")) - .thenReturn(Optional.of(expected)); + Mockito.when(conceptService.conceptDetail("my_dataset", "/foo//bar")).thenReturn(Optional.of(expected)); ResponseEntity actual = subject.conceptDetail("my_dataset", "/foo//bar"); @@ -67,8 +62,7 @@ void shouldGetConceptDetails() { @Test void shouldNotGetConceptDetails() { - Mockito.when(conceptService.conceptDetail("my_dataset", "/foo//asdsad")) - .thenReturn(Optional.empty()); + Mockito.when(conceptService.conceptDetail("my_dataset", "/foo//asdsad")).thenReturn(Optional.empty()); ResponseEntity actual = subject.conceptDetail("my_dataset", "/foo//bar"); @@ -79,13 +73,11 @@ void shouldNotGetConceptDetails() { void shouldGetConceptTree() { Concept fooBar = new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), true, "", List.of(), Map.of()); - Concept fooBaz = - new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()); + Concept fooBaz = new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()); CategoricalConcept foo = new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), true, "", List.of(fooBar, fooBaz), Map.of()); - Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1)) - .thenReturn(Optional.of(foo)); + Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1)).thenReturn(Optional.of(foo)); ResponseEntity actual = subject.conceptTree("my_dataset", "/foo", 1); @@ -97,13 +89,11 @@ void shouldGetConceptTree() { void shouldGetNotConceptTreeForLargeDepth() { Concept fooBar = new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), true, "", List.of(), Map.of()); - Concept fooBaz = - new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()); + Concept fooBaz = new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()); CategoricalConcept foo = new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), true, "", List.of(fooBar, fooBaz), Map.of()); - Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1)) - .thenReturn(Optional.of(foo)); + Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1)).thenReturn(Optional.of(foo)); // concept.tree.max_depth=1 ResponseEntity actual = subject.conceptTree("my_dataset", "/foo//bar", 2); @@ -115,12 +105,10 @@ void shouldGetNotConceptTreeForLargeDepth() { void shouldGetNotConceptTreeForNegativeDepth() { Concept fooBar = new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), true, "", List.of(), Map.of()); - Concept fooBaz = - new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()); + Concept fooBaz = new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()); CategoricalConcept foo = new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), true, "", List.of(fooBar, fooBaz), Map.of()); - Mockito.when(conceptService.conceptTree("my_dataset", "/foo", -1)) - .thenReturn(Optional.of(foo)); + Mockito.when(conceptService.conceptTree("my_dataset", "/foo", -1)).thenReturn(Optional.of(foo)); // concept.tree.max_depth=1 ResponseEntity actual = subject.conceptTree("my_dataset", "/foo//bar", 2); @@ -132,13 +120,11 @@ void shouldGetNotConceptTreeForNegativeDepth() { void shouldNotGetConceptTreeWhenConceptDNE() { Concept fooBar = new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), true, "", List.of(), Map.of()); - Concept fooBaz = - new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()); + Concept fooBaz = new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of()); CategoricalConcept foo = new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), true, "", List.of(fooBar, fooBaz), Map.of()); - Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1)) - .thenReturn(Optional.of(foo)); + Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1)).thenReturn(Optional.of(foo)); ResponseEntity actual = subject.conceptTree("my_dataset", "/asdsadasd", 1); @@ -148,21 +134,16 @@ void shouldNotGetConceptTreeWhenConceptDNE() { @Test void shouldDumpConcepts() { Concept fooBar = new CategoricalConcept( - "/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), true, "", List.of(), - Map.of("key", "value") - ); - Concept fooBaz = new ContinuousConcept( - "/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", - Map.of("key", "value") + "/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), true, "", List.of(), Map.of("key", "value") ); + Concept fooBaz = new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", true, 0F, 100F, "", Map.of("key", "value")); List concepts = List.of(fooBar, fooBaz); PageRequest page = PageRequest.of(0, 10); - Mockito.when(conceptService.listDetailedConcepts(new Filter(List.of(), "", List.of()), page)) - .thenReturn(concepts); + Mockito.when(conceptService.listDetailedConcepts(new Filter(List.of(), "", List.of()), page)).thenReturn(concepts); ResponseEntity> actual = subject.dumpConcepts(0, 10); Assertions.assertEquals(concepts, actual.getBody().getContent()); Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtilTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtilTest.java index 8df3c5c..3b1da9e 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtilTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtilTest.java @@ -16,4 +16,4 @@ void shouldParseValues() { Assertions.assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptServiceTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptServiceTest.java index 216b799..ba1b9cb 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptServiceTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptServiceTest.java @@ -32,13 +32,10 @@ class ConceptServiceTest { @Test void shouldListConcepts() { - List expected = List.of( - new CategoricalConcept("A", "a", "A", "invalid.invalid", null, List.of(), true, "", null, null) - ); + List expected = List.of(new CategoricalConcept("A", "a", "A", "invalid.invalid", null, List.of(), true, "", null, null)); Filter filter = new Filter(List.of(), "", List.of()); Pageable page = Pageable.ofSize(10).first(); - Mockito.when(repository.getConcepts(filter, page)) - .thenReturn(expected); + Mockito.when(repository.getConcepts(filter, page)).thenReturn(expected); List actual = subject.listConcepts(filter, page); @@ -48,8 +45,7 @@ void shouldListConcepts() { @Test void shouldCountConcepts() { Filter filter = new Filter(List.of(), "", List.of()); - Mockito.when(repository.countConcepts(filter)) - .thenReturn(1L); + Mockito.when(repository.countConcepts(filter)).thenReturn(1L); long actual = subject.countConcepts(filter); @@ -60,12 +56,9 @@ void shouldCountConcepts() { void shouldShowDetailForContinuous() { ContinuousConcept concept = new ContinuousConcept("path", "", "", "dataset", null, true, 0F, 1F, "", null); Map meta = Map.of("MIN", "0", "MAX", "1", "stigmatizing", "true"); - Mockito.when(repository.getConcept("dataset", "path")) - .thenReturn(Optional.of(concept)); - Mockito.when(decoratorService.populateParentConcepts(Mockito.any())) - .thenAnswer(i -> i.getArguments()[0]); - Mockito.when(repository.getConceptMeta("dataset", "path")) - .thenReturn(meta); + Mockito.when(repository.getConcept("dataset", "path")).thenReturn(Optional.of(concept)); + Mockito.when(decoratorService.populateParentConcepts(Mockito.any())).thenAnswer(i -> i.getArguments()[0]); + Mockito.when(repository.getConceptMeta("dataset", "path")).thenReturn(meta); Optional actual = subject.conceptDetail("dataset", "path"); Optional expected = Optional.of(new ContinuousConcept(concept, meta)); @@ -77,12 +70,9 @@ void shouldShowDetailForContinuous() { void shouldShowDetailForCategorical() { CategoricalConcept concept = new CategoricalConcept("path", "", "", "dataset", null, List.of("a"), true, "", List.of(), null); Map meta = Map.of("VALUES", "a", "stigmatizing", "true"); - Mockito.when(repository.getConcept("dataset", "path")) - .thenReturn(Optional.of(concept)); - Mockito.when(decoratorService.populateParentConcepts(Mockito.any())) - .thenAnswer(i -> i.getArguments()[0]); - Mockito.when(repository.getConceptMeta("dataset", "path")) - .thenReturn(meta); + Mockito.when(repository.getConcept("dataset", "path")).thenReturn(Optional.of(concept)); + Mockito.when(decoratorService.populateParentConcepts(Mockito.any())).thenAnswer(i -> i.getArguments()[0]); + Mockito.when(repository.getConceptMeta("dataset", "path")).thenReturn(meta); Optional actual = subject.conceptDetail("dataset", "path"); Optional expected = Optional.of(new CategoricalConcept(concept, meta)); @@ -105,31 +95,24 @@ void shouldShowDetailForMultiple() { Filter emptyFilter = new Filter(List.of(), "", List.of()); - Mockito.when(repository.getConceptMetaForConcepts(concepts)) - .thenReturn(metas); - Mockito.when(repository.getConcepts(emptyFilter, Pageable.unpaged())) - .thenReturn(concepts); + Mockito.when(repository.getConceptMetaForConcepts(concepts)).thenReturn(metas); + Mockito.when(repository.getConcepts(emptyFilter, Pageable.unpaged())).thenReturn(concepts); List actual = subject.listDetailedConcepts(emptyFilter, Pageable.unpaged()); - List expected = List.of( - new CategoricalConcept(conceptA, metaA), - new ContinuousConcept(conceptB, metaB) - ); + List expected = List.of(new CategoricalConcept(conceptA, metaA), new ContinuousConcept(conceptB, metaB)); Assertions.assertEquals(expected, actual); } @Test void shouldGetTree() { - CategoricalConcept concept = new CategoricalConcept("ds", "\\A\\B\\C\\").withChildren( - List.of(new CategoricalConcept("ds", "\\A\\B\\C\\1\\"), new ContinuousConcept("ds", "\\A\\B\\C\\2\\")) - ); - Mockito.when(repository.getConceptTree("ds", "\\A\\B\\C\\", 2)) - .thenReturn(Optional.of(concept)); + CategoricalConcept concept = new CategoricalConcept("ds", "\\A\\B\\C\\") + .withChildren(List.of(new CategoricalConcept("ds", "\\A\\B\\C\\1\\"), new ContinuousConcept("ds", "\\A\\B\\C\\2\\"))); + Mockito.when(repository.getConceptTree("ds", "\\A\\B\\C\\", 2)).thenReturn(Optional.of(concept)); Optional actual = subject.conceptTree("ds", "\\A\\B\\C\\", 2); Assertions.assertTrue(actual.isPresent()); Assertions.assertEquals(concept, actual.get()); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTest.java index 85aaa03..b469338 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTest.java @@ -14,7 +14,8 @@ class ConceptTest { @Test void shouldRoundTrip() throws JsonProcessingException { - Concept expected = new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", null, List.of("a", "b"), true, "", List.of(), Map.of()); + Concept expected = + new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", null, List.of("a", "b"), true, "", List.of(), Map.of()); String json = objectMapper.writeValueAsString(expected); Concept actual = objectMapper.readValue(json, Concept.class); @@ -37,7 +38,8 @@ void shouldReadCategorical() throws JsonProcessingException { } """; - CategoricalConcept expected = new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", null, List.of("a", "b"), true, "", null, Map.of()); + CategoricalConcept expected = + new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", null, List.of("a", "b"), true, "", null, Map.of()); Concept actual = new ObjectMapper().readValue(json, Concept.class); Assertions.assertEquals(expected, actual); @@ -75,8 +77,9 @@ void shouldIncludeTypeInList() throws JsonProcessingException { ); String actual = new ObjectMapper().writeValueAsString(concepts); - String expected = "[{\"conceptPath\":\"/foo//baz\",\"name\":\"baz\",\"display\":\"Baz\",\"dataset\":\"study_a\",\"description\":null,\"allowFiltering\":true,\"min\":0.0,\"max\":1.0,\"studyAcronym\":\"\",\"meta\":{},\"children\":null,\"table\":null,\"study\":null,\"type\":\"Continuous\"},{\"conceptPath\":\"/foo//bar\",\"name\":\"bar\",\"display\":\"Bar\",\"dataset\":\"study_a\",\"description\":null,\"values\":[\"a\",\"b\"],\"allowFiltering\":true,\"studyAcronym\":\"\",\"children\":null,\"meta\":{},\"table\":null,\"study\":null,\"type\":\"Categorical\"}]"; + String expected = + "[{\"conceptPath\":\"/foo//baz\",\"name\":\"baz\",\"display\":\"Baz\",\"dataset\":\"study_a\",\"description\":null,\"allowFiltering\":true,\"min\":0.0,\"max\":1.0,\"studyAcronym\":\"\",\"meta\":{},\"children\":null,\"table\":null,\"study\":null,\"type\":\"Continuous\"},{\"conceptPath\":\"/foo//bar\",\"name\":\"bar\",\"display\":\"Bar\",\"dataset\":\"study_a\",\"description\":null,\"values\":[\"a\",\"b\"],\"allowFiltering\":true,\"studyAcronym\":\"\",\"children\":null,\"meta\":{},\"table\":null,\"study\":null,\"type\":\"Categorical\"}]"; Assertions.assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTypeTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTypeTest.java index 660f1a3..76219c9 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTypeTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTypeTest.java @@ -14,4 +14,4 @@ void shouldGetValueOf() { Assertions.assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardConfigTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardConfigTest.java index a75859a..397a055 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardConfigTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardConfigTest.java @@ -17,13 +17,11 @@ class DashboardConfigTest { void shouldGetColumns() { List actual = subject.getColumns(); List expected = List.of( - new DashboardColumn("abbreviation", "Abbreviation"), - new DashboardColumn("name", "Name"), - new DashboardColumn("clinvars", "Clinical Variables"), - new DashboardColumn("melast", "This one goes last"), + new DashboardColumn("abbreviation", "Abbreviation"), new DashboardColumn("name", "Name"), + new DashboardColumn("clinvars", "Clinical Variables"), new DashboardColumn("melast", "This one goes last"), new DashboardColumn("participants", "Participants") ); Assertions.assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardControllerTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardControllerTest.java index 96c5f13..ec11139 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardControllerTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardControllerTest.java @@ -22,12 +22,11 @@ class DashboardControllerTest { @Test void shouldGetDashboard() { Dashboard dashboard = new Dashboard(List.of(), List.of()); - Mockito.when(service.getDashboard()) - .thenReturn(dashboard); + Mockito.when(service.getDashboard()).thenReturn(dashboard); ResponseEntity actual = subject.getDashboard(); Assertions.assertEquals(dashboard, actual.getBody()); Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepositoryTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepositoryTest.java index f788bdd..83c0a1d 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepositoryTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepositoryTest.java @@ -22,12 +22,8 @@ class DashboardRepositoryTest { DashboardRepository subject; @Container - static final PostgreSQLContainer databaseContainer = - new PostgreSQLContainer<>("postgres:16") - .withReuse(true) - .withCopyFileToContainer( - MountableFile.forClasspathResource("seed.sql"), "/docker-entrypoint-initdb.d/seed.sql" - ); + static final PostgreSQLContainer databaseContainer = new PostgreSQLContainer<>("postgres:16").withReuse(true) + .withCopyFileToContainer(MountableFile.forClasspathResource("seed.sql"), "/docker-entrypoint-initdb.d/seed.sql"); @DynamicPropertySource static void mySQLProperties(DynamicPropertyRegistry registry) { @@ -41,15 +37,38 @@ static void mySQLProperties(DynamicPropertyRegistry registry) { void shouldGetDashboardRows() { List> actual = subject.getRows(); List> expected = List.of( - Map.of("name", "DEMENTIA-SEQ: WGS in Lewy Body Dementia and Frontotemporal Dementia", "abbreviation", "DEMENTIA-SEQ", "melast", "", "clinvars", "653", "participants", "65"), - Map.of("name", "Framingham Cohort", "abbreviation", "FHS", "melast", "", "clinvars", "12546", "participants", "3435"), - Map.of("name", "Hematopoietic Cell Transplant for Sickle Cell Disease (HCT for SCD)", "abbreviation", "HCT_for_SCD", "melast", "", "clinvars", "7567", "participants", "33"), - Map.of("name", "National Sleep Research Resource (NSRR): Cleveland Family Study (CFS)", "abbreviation", "NSRR CFS", "melast", "", "clinvars", "500", "participants", "23432"), - Map.of("name", "National Sleep Research Resource (NSRR): (HSHC)", "abbreviation", "NSRR_HSHC", "melast", "", "clinvars", "434", "participants", "53435"), - Map.of("name", "NHLBI Cleveland Family Study (CFS) Candidate Gene Association Resource (CARe)", "abbreviation", "CFS", "melast", "", "clinvars", "12321", "participants", "867876"), - Map.of("name", "Nulliparous Pregnancy Outcomes Study: Monitoring Mothers-to-be Heart Health Study (nuMoM2b Heart Health Study)", "abbreviation", "nuMoM2b", "melast", "", "clinvars", "2", "participants", "111"), - Map.of("name", "Researching COVID to Enhance Recovery (RECOVER): Adult Observational Cohort Study", "abbreviation", "RECOVER_Adult", "melast", "", "clinvars", "654645", "participants", "6654"), - Map.of("name", "Systolic Blood Pressure Intervention Trial (SPRINT-Imaging)", "abbreviation", "SPRINT", "melast", "", "clinvars", "333", "participants", "2222") + Map.of( + "name", "DEMENTIA-SEQ: WGS in Lewy Body Dementia and Frontotemporal Dementia", "abbreviation", "DEMENTIA-SEQ", "melast", "", + "clinvars", "653", "participants", "65" + ), Map.of("name", "Framingham Cohort", "abbreviation", "FHS", "melast", "", "clinvars", "12546", "participants", "3435"), + Map.of( + "name", "Hematopoietic Cell Transplant for Sickle Cell Disease (HCT for SCD)", "abbreviation", "HCT_for_SCD", "melast", "", + "clinvars", "7567", "participants", "33" + ), + Map.of( + "name", "National Sleep Research Resource (NSRR): Cleveland Family Study (CFS)", "abbreviation", "NSRR CFS", "melast", "", + "clinvars", "500", "participants", "23432" + ), + Map.of( + "name", "National Sleep Research Resource (NSRR): (HSHC)", "abbreviation", "NSRR_HSHC", "melast", "", "clinvars", "434", + "participants", "53435" + ), + Map.of( + "name", "NHLBI Cleveland Family Study (CFS) Candidate Gene Association Resource (CARe)", "abbreviation", "CFS", "melast", + "", "clinvars", "12321", "participants", "867876" + ), + Map.of( + "name", "Nulliparous Pregnancy Outcomes Study: Monitoring Mothers-to-be Heart Health Study (nuMoM2b Heart Health Study)", + "abbreviation", "nuMoM2b", "melast", "", "clinvars", "2", "participants", "111" + ), + Map.of( + "name", "Researching COVID to Enhance Recovery (RECOVER): Adult Observational Cohort Study", "abbreviation", + "RECOVER_Adult", "melast", "", "clinvars", "654645", "participants", "6654" + ), + Map.of( + "name", "Systolic Blood Pressure Intervention Trial (SPRINT-Imaging)", "abbreviation", "SPRINT", "melast", "", "clinvars", + "333", "participants", "2222" + ) ); Assertions.assertEquals(expected, actual); } @@ -59,4 +78,4 @@ void shouldDoBDCHack() { List> rows = subject.getHackyBDCRows(); Assertions.assertNotNull(rows); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardServiceTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardServiceTest.java index edf4c3f..1825e21 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardServiceTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardServiceTest.java @@ -24,12 +24,11 @@ class DashboardServiceTest { @Test void shouldGetDashboard() { List> rows = List.of(Map.of("a", "1", "b", "2")); - Mockito.when(repository.getRows()) - .thenReturn(rows); + Mockito.when(repository.getRows()).thenReturn(rows); Dashboard actual = subject.getDashboard(); Dashboard expected = new Dashboard(columns, rows); Assertions.assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetControllerTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetControllerTest.java index 99a3995..331f176 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetControllerTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetControllerTest.java @@ -26,15 +26,11 @@ class FacetControllerTest { void shouldListFacets() { Facet questionnaire = new Facet("questionnaire", "Questionnaire", "questionnaire", "Questionnaire", 1, null, "category", null); Facet examination = new Facet("examination", "Examination", "examination", "Examination", 1, null, "category", null); - FacetCategory expected = - new FacetCategory("category", "Category", "categories!", List.of(questionnaire, examination)); + FacetCategory expected = new FacetCategory("category", "Category", "categories!", List.of(questionnaire, examination)); - Filter filter = new Filter( - List.of(new Facet("questionare", "Questionare", "?", "Examination", 1, null, "category", null)), - "foo", List.of() - ); - Mockito.when(facetService.getFacets(filter)) - .thenReturn(List.of(expected)); + Filter filter = + new Filter(List.of(new Facet("questionare", "Questionare", "?", "Examination", 1, null, "category", null)), "foo", List.of()); + Mockito.when(facetService.getFacets(filter)).thenReturn(List.of(expected)); ResponseEntity> actual = subject.getFacets(filter); @@ -45,8 +41,7 @@ void shouldListFacets() { @Test void shouldGetFacetDetails() { Facet expected = new Facet("questionnaire", "Questionnaire", "questionnaire", "Questionare", 1, null, "category", null); - Mockito.when(facetService.facetDetails("category", "questionnaire")) - .thenReturn(Optional.of(expected)); + Mockito.when(facetService.facetDetails("category", "questionnaire")).thenReturn(Optional.of(expected)); ResponseEntity actual = subject.facetDetails("category", "questionnaire"); @@ -57,11 +52,10 @@ void shouldGetFacetDetails() { @Test void shouldNotGetFacetDetails() { Facet questionnaire = new Facet("questionnaire", "Questionnaire", "questionnaire", "Questionare", 1, null, "category", null); - Mockito.when(facetService.facetDetails("category", "questionnaire")) - .thenReturn(Optional.of(questionnaire)); + Mockito.when(facetService.facetDetails("category", "questionnaire")).thenReturn(Optional.of(questionnaire)); ResponseEntity actual = subject.facetDetails("category", "brungus"); Assertions.assertEquals(HttpStatus.NOT_FOUND, actual.getStatusCode()); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetQueryGeneratorTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetQueryGeneratorTest.java index 092b575..c9d8b59 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetQueryGeneratorTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetQueryGeneratorTest.java @@ -30,12 +30,8 @@ class FacetQueryGeneratorTest { FacetQueryGenerator subject; @Container - static final PostgreSQLContainer databaseContainer = - new PostgreSQLContainer<>("postgres:16") - .withReuse(true) - .withCopyFileToContainer( - MountableFile.forClasspathResource("seed.sql"), "/docker-entrypoint-initdb.d/seed.sql" - ); + static final PostgreSQLContainer databaseContainer = new PostgreSQLContainer<>("postgres:16").withReuse(true) + .withCopyFileToContainer(MountableFile.forClasspathResource("seed.sql"), "/docker-entrypoint-initdb.d/seed.sql"); @DynamicPropertySource static void mySQLProperties(DynamicPropertyRegistry registry) { @@ -45,7 +41,8 @@ static void mySQLProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.db", databaseContainer::getDatabaseName); } - record IdCountPair(int facetId, int facetCount) {} + record IdCountPair(int facetId, int facetCount) { + } static class IdCountPairMapper implements RowMapper { @@ -64,9 +61,8 @@ void shouldCountFacetsWithNoSearchAndNoSelectedFacetsAndNoConsents() { List actual = template.query(query, params, new IdCountPairMapper()); List expected = List.of( - new IdCountPair(22, 13), new IdCountPair(31, 3), new IdCountPair(27, 3), - new IdCountPair(26, 3), new IdCountPair(28, 3), new IdCountPair(23, 2), - new IdCountPair(25, 2), new IdCountPair(21, 1), new IdCountPair(20, 1) + new IdCountPair(22, 13), new IdCountPair(31, 3), new IdCountPair(27, 3), new IdCountPair(26, 3), new IdCountPair(28, 3), + new IdCountPair(23, 2), new IdCountPair(25, 2), new IdCountPair(21, 1), new IdCountPair(20, 1) ); Assertions.assertEquals(expected, actual); @@ -80,9 +76,7 @@ void shouldCountFacetsWithNoSearchAndNoSelectedFacetsAndConsents() { String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(27, 3), new IdCountPair(20, 1), new IdCountPair(21, 1) - ); + List expected = List.of(new IdCountPair(27, 3), new IdCountPair(20, 1), new IdCountPair(21, 1)); Assertions.assertEquals(expected, actual); } @@ -95,12 +89,8 @@ void shouldCountFacetsWithSearchAndNoSelectedFacetsAndNoConsents() { String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(23, 1), - new IdCountPair(25, 1), - new IdCountPair(26, 1), - new IdCountPair(28, 1) - ); + List expected = + List.of(new IdCountPair(23, 1), new IdCountPair(25, 1), new IdCountPair(26, 1), new IdCountPair(28, 1)); Assertions.assertEquals(expected, actual); } @@ -113,32 +103,21 @@ void shouldCountFacetsWithSearchAndNoSelectedFacetsAndConsents() { String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(25, 1), - new IdCountPair(26, 1), - new IdCountPair(28, 1) - ); + List expected = List.of(new IdCountPair(25, 1), new IdCountPair(26, 1), new IdCountPair(28, 1)); Assertions.assertEquals(expected, actual); } @Test void shouldCountFacetsWithSearchAndOneSelectedFacetsAndNoConsents() { - Filter filter = new Filter( - List.of(new Facet("phs002715", "study_ids_dataset_ids")), - "age", List.of() - ); + Filter filter = new Filter(List.of(new Facet("phs002715", "study_ids_dataset_ids")), "age", List.of()); MapSqlParameterSource params = new MapSqlParameterSource(); String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(23, 1), - new IdCountPair(25, 1), - new IdCountPair(26, 1), - new IdCountPair(28, 1) - ); + List expected = + List.of(new IdCountPair(23, 1), new IdCountPair(25, 1), new IdCountPair(26, 1), new IdCountPair(28, 1)); Assertions.assertEquals(expected, actual); } @@ -146,42 +125,29 @@ void shouldCountFacetsWithSearchAndOneSelectedFacetsAndNoConsents() { @Test void shouldCountFacetsWithSearchAndOneSelectedFacetsAndConsents() { Filter filter = new Filter( - List.of(new Facet("phs002715", "study_ids_dataset_ids")), - "age", List.of("phs002715.c1", "phs000284.c1", "phs002385.c1") + List.of(new Facet("phs002715", "study_ids_dataset_ids")), "age", List.of("phs002715.c1", "phs000284.c1", "phs002385.c1") ); MapSqlParameterSource params = new MapSqlParameterSource(); String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(25, 1), - new IdCountPair(26, 1), - new IdCountPair(28, 1) - ); + List expected = List.of(new IdCountPair(25, 1), new IdCountPair(26, 1), new IdCountPair(28, 1)); Assertions.assertEquals(expected, actual); } @Test void shouldCountFacetsNoSearchAndOneSelectedFacetsAndNoConsents() { - Filter filter = new Filter( - List.of(new Facet("phs002715", "study_ids_dataset_ids")), - "", List.of() - ); + Filter filter = new Filter(List.of(new Facet("phs002715", "study_ids_dataset_ids")), "", List.of()); MapSqlParameterSource params = new MapSqlParameterSource(); String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); List expected = List.of( - new IdCountPair(28, 3), - new IdCountPair(26, 3), - new IdCountPair(31, 3), - new IdCountPair(22, 13), - new IdCountPair(23, 2), - new IdCountPair(25, 2), - new IdCountPair(27, 3) + new IdCountPair(28, 3), new IdCountPair(26, 3), new IdCountPair(31, 3), new IdCountPair(22, 13), new IdCountPair(23, 2), + new IdCountPair(25, 2), new IdCountPair(27, 3) ); Assertions.assertEquals(expected, actual); @@ -189,41 +155,27 @@ void shouldCountFacetsNoSearchAndOneSelectedFacetsAndNoConsents() { @Test void shouldCountFacetsNoSearchAndOneSelectedFacetsAndConsents() { - Filter filter = new Filter( - List.of(new Facet("phs002715", "study_ids_dataset_ids")), - "", List.of("phs000007.c2") - ); + Filter filter = new Filter(List.of(new Facet("phs002715", "study_ids_dataset_ids")), "", List.of("phs000007.c2")); MapSqlParameterSource params = new MapSqlParameterSource(); String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(27, 3) - ); + List expected = List.of(new IdCountPair(27, 3)); Assertions.assertEquals(expected, actual); } @Test void shouldCountFacetsWithSearchAndTwoSelectedFacetsInDifferentCatsAndNoConsents() { - Filter filter = new Filter( - List.of( - new Facet("phs000007", "study_ids_dataset_ids"), - new Facet("LOINC", "nsrr_harmonized") - ), - "cola", List.of() - ); + Filter filter = + new Filter(List.of(new Facet("phs000007", "study_ids_dataset_ids"), new Facet("LOINC", "nsrr_harmonized")), "cola", List.of()); MapSqlParameterSource params = new MapSqlParameterSource(); String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(21, 1), - new IdCountPair(27, 1), - new IdCountPair(20, 1) - ); + List expected = List.of(new IdCountPair(21, 1), new IdCountPair(27, 1), new IdCountPair(20, 1)); Assertions.assertEquals(expected, actual); } @@ -231,45 +183,29 @@ void shouldCountFacetsWithSearchAndTwoSelectedFacetsInDifferentCatsAndNoConsents @Test void shouldCountFacetsWithSearchAndTwoSelectedFacetsInDifferentCatsAndConsents() { Filter filter = new Filter( - List.of( - new Facet("phs000007", "study_ids_dataset_ids"), - new Facet("LOINC", "nsrr_harmonized") - ), - "cola", List.of("LOINC.c1", "PhenX.c1", "phs000007.c1") + List.of(new Facet("phs000007", "study_ids_dataset_ids"), new Facet("LOINC", "nsrr_harmonized")), "cola", + List.of("LOINC.c1", "PhenX.c1", "phs000007.c1") ); MapSqlParameterSource params = new MapSqlParameterSource(); String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(21, 1), - new IdCountPair(27, 1), - new IdCountPair(20, 1) - ); + List expected = List.of(new IdCountPair(21, 1), new IdCountPair(27, 1), new IdCountPair(20, 1)); Assertions.assertEquals(expected, actual); } @Test void shouldCountFacetsNoSearchAndTwoSelectedFacetsInDifferentCatsAndNoConsents() { - Filter filter = new Filter( - List.of( - new Facet("phs000007", "study_ids_dataset_ids"), - new Facet("LOINC", "nsrr_harmonized") - ), - "", List.of() - ); + Filter filter = + new Filter(List.of(new Facet("phs000007", "study_ids_dataset_ids"), new Facet("LOINC", "nsrr_harmonized")), "", List.of()); MapSqlParameterSource params = new MapSqlParameterSource(); String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(21, 1), - new IdCountPair(27, 1), - new IdCountPair(20, 1) - ); + List expected = List.of(new IdCountPair(21, 1), new IdCountPair(27, 1), new IdCountPair(20, 1)); Assertions.assertEquals(expected, actual); } @@ -277,23 +213,16 @@ void shouldCountFacetsNoSearchAndTwoSelectedFacetsInDifferentCatsAndNoConsents() @Test void shouldCountFacetsNoSearchAndTwoSelectedFacetsInDifferentCatsAndConsents() { Filter filter = new Filter( - List.of( - new Facet("phs000007", "study_ids_dataset_ids"), - new Facet("LOINC", "nsrr_harmonized") - ), - "", List.of("LOINC.c1", "PhenX.c1", "phs000007.c1") + List.of(new Facet("phs000007", "study_ids_dataset_ids"), new Facet("LOINC", "nsrr_harmonized")), "", + List.of("LOINC.c1", "PhenX.c1", "phs000007.c1") ); MapSqlParameterSource params = new MapSqlParameterSource(); String query = subject.createFacetSQLAndPopulateParams(filter, params); List actual = template.query(query, params, new IdCountPairMapper()); - List expected = List.of( - new IdCountPair(21, 1), - new IdCountPair(27, 1), - new IdCountPair(20, 1) - ); + List expected = List.of(new IdCountPair(21, 1), new IdCountPair(27, 1), new IdCountPair(20, 1)); Assertions.assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetServiceTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetServiceTest.java index 55bad26..84da8a8 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetServiceTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetServiceTest.java @@ -25,8 +25,7 @@ void shouldGetFacets() { Filter filter = new Filter(List.of(), "", List.of()); List expected = List.of(new FacetCategory("n", "d", "", List.of(new Facet("f_n", "f_d", "", "", 1, null, "n", null)))); - Mockito.when(repository.getFacets(filter)) - .thenReturn(expected); + Mockito.when(repository.getFacets(filter)).thenReturn(expected); List actual = subject.getFacets(filter); @@ -36,13 +35,11 @@ void shouldGetFacets() { @Test void shouldGetFacet() { Optional expected = Optional.of(new Facet("n", "d", "", "", null, null, "c", Map.of("foo", "bar"))); - Mockito.when(repository.getFacet("c", "n")) - .thenReturn(expected); - Mockito.when(repository.getFacetMeta("c", "n")) - .thenReturn(Map.of("foo", "bar")); + Mockito.when(repository.getFacet("c", "n")).thenReturn(expected); + Mockito.when(repository.getFacetMeta("c", "n")).thenReturn(Map.of("foo", "bar")); Optional actual = subject.facetDetails("c", "n"); Assertions.assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FilterPreProcessorTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FilterPreProcessorTest.java index 628c72e..5fa6150 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FilterPreProcessorTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FilterPreProcessorTest.java @@ -21,9 +21,8 @@ class FilterPreProcessorTest { @Test void shouldProcessFilter() { Object processedFilter = subject.afterBodyRead( - new Filter(List.of(), "I_love_underscores", List.of()), - Mockito.mock(HttpInputMessage.class), Mockito.mock(MethodParameter.class), - SimpleType.constructUnsafe(Filter.class), null + new Filter(List.of(), "I_love_underscores", List.of()), Mockito.mock(HttpInputMessage.class), + Mockito.mock(MethodParameter.class), SimpleType.constructUnsafe(Filter.class), null ); Assertions.assertEquals(new Filter(List.of(), "I/love/underscores", List.of()), processedFilter); @@ -32,11 +31,10 @@ void shouldProcessFilter() { @Test void shouldNotProcessOtherBodies() { Object actual = subject.afterBodyRead( - "I'm an object!", - Mockito.mock(HttpInputMessage.class), Mockito.mock(MethodParameter.class), + "I'm an object!", Mockito.mock(HttpInputMessage.class), Mockito.mock(MethodParameter.class), SimpleType.constructUnsafe(Filter.class), null ); Assertions.assertEquals("I'm an object!", actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/filter/ConceptFilterQueryGeneratorTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/filter/ConceptFilterQueryGeneratorTest.java index 0bc075b..a9b4815 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/filter/ConceptFilterQueryGeneratorTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/filter/ConceptFilterQueryGeneratorTest.java @@ -23,12 +23,8 @@ class ConceptFilterQueryGeneratorTest { @Container - static final PostgreSQLContainer databaseContainer = - new PostgreSQLContainer<>("postgres:16") - .withReuse(true) - .withCopyFileToContainer( - MountableFile.forClasspathResource("seed.sql"), "/docker-entrypoint-initdb.d/seed.sql" - ); + static final PostgreSQLContainer databaseContainer = new PostgreSQLContainer<>("postgres:16").withReuse(true) + .withCopyFileToContainer(MountableFile.forClasspathResource("seed.sql"), "/docker-entrypoint-initdb.d/seed.sql"); @DynamicPropertySource static void mySQLProperties(DynamicPropertyRegistry registry) { @@ -69,7 +65,8 @@ void shouldGenerateForHarmonizedConsents() { @Test void shouldGenerateForFacetAndSearchNoMatch() { - Filter f = new Filter(List.of(new Facet("phs000007", "FHS", "", "", null, null, "study_ids_dataset_ids", null)), "smoke", List.of()); + Filter f = + new Filter(List.of(new Facet("phs000007", "FHS", "", "", null, null, "study_ids_dataset_ids", null)), "smoke", List.of()); QueryParamPair pair = subject.generateFilterQuery(f, Pageable.unpaged()); String query = "WITH " + pair.query() + "\n SELECT concept_node_id FROM concepts_filtered_sorted;"; @@ -93,9 +90,8 @@ void shouldGenerateForFHSFacet() { @Test void shouldGenerateForFHSFacetWithConsent1() { - Filter f = new Filter(List.of(new Facet( - "phs000007", "FHS", "", "", null, null, "study_ids_dataset_ids", null)), - "", List.of("phs000007.c1") + Filter f = new Filter( + List.of(new Facet("phs000007", "FHS", "", "", null, null, "study_ids_dataset_ids", null)), "", List.of("phs000007.c1") ); QueryParamPair pair = subject.generateFilterQuery(f, Pageable.unpaged()); String query = "WITH " + pair.query() + "\n SELECT concept_node_id FROM concepts_filtered_sorted;"; @@ -108,7 +104,10 @@ void shouldGenerateForFHSFacetWithConsent1() { @Test void shouldGenerateForFHSFacetWithConsent1And2() { - Filter f = new Filter(List.of(new Facet("phs000007", "FHS", "", "", null, null, "study_ids_dataset_ids", null)), "", List.of("phs000007.c1", "phs000007.c2")); + Filter f = new Filter( + List.of(new Facet("phs000007", "FHS", "", "", null, null, "study_ids_dataset_ids", null)), "", + List.of("phs000007.c1", "phs000007.c2") + ); QueryParamPair pair = subject.generateFilterQuery(f, Pageable.unpaged()); String query = "WITH " + pair.query() + "\n SELECT concept_node_id FROM concepts_filtered_sorted;"; @@ -120,7 +119,8 @@ void shouldGenerateForFHSFacetWithConsent1And2() { @Test void shouldGenerateForFHSFacetWithConsent3() { - Filter f = new Filter(List.of(new Facet("phs000007", "FHS", "", "", null, null, "study_ids_dataset_ids", null)), "", List.of("dne.c3")); + Filter f = + new Filter(List.of(new Facet("phs000007", "FHS", "", "", null, null, "study_ids_dataset_ids", null)), "", List.of("dne.c3")); QueryParamPair pair = subject.generateFilterQuery(f, Pageable.unpaged()); String query = "WITH " + pair.query() + "\n SELECT concept_node_id FROM concepts_filtered_sorted;"; @@ -132,7 +132,8 @@ void shouldGenerateForFHSFacetWithConsent3() { @Test void shouldGenerateForFacetAndSearchMatch() { - Filter f = new Filter(List.of(new Facet("phs002715", "NSRR", "", "", null, null, "study_ids_dataset_ids", null)), "smoke", List.of()); + Filter f = + new Filter(List.of(new Facet("phs002715", "NSRR", "", "", null, null, "study_ids_dataset_ids", null)), "smoke", List.of()); QueryParamPair pair = subject.generateFilterQuery(f, Pageable.unpaged()); String query = "WITH " + pair.query() + "\n SELECT concept_node_id FROM concepts_filtered_sorted;"; @@ -153,4 +154,4 @@ void shouldGenerateForNSRRFacet() { Assertions.assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/info/InfoControllerTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/info/InfoControllerTest.java index 0e39cdb..d401db4 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/info/InfoControllerTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/info/InfoControllerTest.java @@ -24,4 +24,4 @@ void shouldGetInfo() { Assertions.assertEquals(HttpStatus.OK, actual.getStatusCode()); Assertions.assertEquals(new InfoResponse(UUID.nameUUIDFromBytes(":)".getBytes()), ":)", List.of()), actual.getBody()); } -} \ No newline at end of file +}