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 39ae032..ce85966 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,20 +16,7 @@ public class ConceptResultSetExtractor implements ResultSetExtractor { @Autowired private ConceptResultSetUtil conceptResultSetUtil; - private record ConceptWithId(Concept c, int id) { - @Override - public boolean equals(Object object) { - if (this == object) return true; - if (object == null || getClass() != object.getClass()) return false; - ConceptWithId conceptWithId = (ConceptWithId) object; - return id == conceptWithId.id; - } - - @Override - public int hashCode() { - return Objects.hashCode(id); - } - }; + private record ConceptWithId(Concept c, int id) {}; @Override public Concept extractData(ResultSet rs) throws SQLException, DataAccessException { 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 d328b79..58dc1a9 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 @@ -17,4 +17,8 @@ public Facet(Facet core, Map meta) { public Facet(String name, String category) { this(name, "", "", "", null, null, category, null); } + + public Facet withChildren(List children) { + return new Facet(this.name, this.display, this.description, this.fullName, this.count, children, this.category, this.meta); + } } 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 7fc3b49..8f6d3c1 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 @@ -2,6 +2,7 @@ import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.util.StringUtils; import java.sql.ResultSet; import java.sql.SQLException; @@ -10,10 +11,17 @@ public class FacetCategoryExtractor implements ResultSetExtractor> { + private record Pair(String parent, String category) { + Pair(Facet facet) { + this(facet.name(), facet.category()); + } + }; + @Override public List extractData(ResultSet rs) throws SQLException, DataAccessException { List facets = new ArrayList<>(); Map categories = new HashMap<>(); + Map> childrenForParent = new HashMap<>(); while (rs.next()) { // build out all the facets and make shells of the facet categories @@ -21,15 +29,26 @@ public List extractData(ResultSet rs) throws SQLException, DataAc Facet facet = new Facet( rs.getString("name"), rs.getString("display"), rs.getString("description"), rs.getString("full_name"), rs.getInt("facet_count"), - null, category, null + List.of(), category, null ); FacetCategory facetCategory = new FacetCategory( category, rs.getString("category_display"), rs.getString("category_description"), List.of() ); - facets.add(facet); + String parentName = rs.getString("parent_name"); + if (StringUtils.hasLength(parentName)) { + Pair key = new Pair(parentName, category); + List facetsForParent = childrenForParent.getOrDefault(key, new ArrayList<>()); + facetsForParent.add(facet); + childrenForParent.put(key, facetsForParent); + } else { + facets.add(facet); + } categories.put(category, facetCategory); } + 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() 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 7b52aa0..c74f988 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 @@ -17,7 +17,7 @@ public Facet mapRow(ResultSet rs, int rowNum) throws SQLException { rs.getString("description"), rs.getString("full_name"), null, - null, + List.of(), rs.getString("category"), null ); 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 c0335a5..1cfd49d 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 @@ -74,7 +74,29 @@ public Optional getFacet(String facetCategory, String facet) { MapSqlParameterSource params = new MapSqlParameterSource() .addValue("facetCategory", facetCategory) .addValue("facetName", facet); - return template.query(sql, params, mapper).stream().findFirst(); + 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); + return template.query(sql, params, mapper); } public Map getFacetMeta(String facetCategory, String facet) { diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepositoryTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepositoryTest.java index 2413526..4978476 100644 --- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepositoryTest.java +++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepositoryTest.java @@ -44,63 +44,56 @@ static void mySQLProperties(DynamicPropertyRegistry registry) { @Test void shouldGetAllFacets() { Filter filter = new Filter(List.of(), "", List.of()); - - List actual = subject.getFacets(filter); - - Assertions.assertEquals(2, actual.size()); - } - - @Test - void shouldFilterFacetsBySearch() { - Filter filter = new Filter(List.of(), "X", List.of()); List actual = subject.getFacets(filter); - List expected = List.of( - new FacetCategory( - "site", "Site", "Filter variables by site", + new FacetCategory("study_ids_dataset_ids", "Study IDs/Dataset IDs", "", List.of( - new Facet("bch", "BCH", "Boston Childrens Hospital", "Boston Childrens Hospital", 1, null, "category", null) + new Facet("1", "GIC", null, null, 13, List.of(), "study_ids_dataset_ids", null), + new Facet("phs000284", "CFS", null, "Chronic Fatigue Syndrome", 3, List.of(), "study_ids_dataset_ids", null), + new Facet("phs000007", "FHS", null, "Framingham Heart Study", 3, List.of(), "study_ids_dataset_ids", null), + new Facet("phs002385", "HCT_for_SCD", null, null, 3, List.of(), "study_ids_dataset_ids", null), + new Facet("phs002808", "nuMoM2b", null, null, 3, List.of(), "study_ids_dataset_ids", null), + new Facet("2", "National Health and Nutrition Examination Survey", null, null, 2, List.of(), "study_ids_dataset_ids", null), + new Facet("phs002715", "NSRR CFS", null, "National Sleep Research Resource", 2, List.of(), "study_ids_dataset_ids", null), + new Facet("3", "1000 Genomes Project", null, null, 0, List.of(), "study_ids_dataset_ids", null), + new Facet("phs003463", "RECOVER_Adult", null, null, 0, List.of(), "study_ids_dataset_ids", null), + new Facet("phs003543", "NSRR_HSHC", null, null, 0, List.of(), "study_ids_dataset_ids", null), + new Facet("phs003566", "SPRINT", null, null, 0, List.of(), "study_ids_dataset_ids", null), + new Facet("phs001963", "DEMENTIA-SEQ", null, null, 0, List.of( + new Facet("NEST_1", "My Nested Facet 1", null, null, 0, List.of(), "study_ids_dataset_ids", null), + new Facet("NEST_2", "My Nested Facet 2", null, null, 0, List.of(), "study_ids_dataset_ids", null) + ), "study_ids_dataset_ids", null) ) ), - new FacetCategory( - "data_source", "Data Source", "What does this data relate to (image, questionnaire...)", + new FacetCategory("nsrr_harmonized", "Common Data Element Collection", "", List.of( - new Facet("imaging", "Imaging", "Data derived from an image", "Data derived from an image", 1, null, "data_source", null), - new Facet("questionnaire", "questionnaire", "Data derived from a questionnaire", "Data derived from a questionnaire", 1, null, "data_source", null), - new Facet("lab_test", "Lab Test", "Data derived from a lab test", "Data derived from a lab test", 1, null, "data_source", null) + new Facet("LOINC", "LOINC", null, null, 1, List.of(), "nsrr_harmonized", null), + new Facet("PhenX", "PhenX", null, null, 1, List.of(), "nsrr_harmonized", null), + new Facet("gad_7", "Generalized Anxiety Disorder Assessment (GAD-7)", null, null, 0, List.of(), "nsrr_harmonized", null), + new Facet("taps_tool", "NIDA CTN Common Data Elements = TAPS Tool", null, null, 0, List.of(), "nsrr_harmonized", null) ) ) ); + + Assertions.assertEquals(expected, actual); } @Test - void shouldFilterFacetsByFacet() { - Filter filter = new Filter(List.of(new Facet("bch", "BCH", "Boston Childrens Hospital", "Boston Childrens Hospital", 1, null, "category", null)), "", List.of()); - List actual = subject.getFacets(filter); - - List expected = List.of( - new FacetCategory( - "site", "Site", "Filter variables by site", - List.of( - new Facet("bch", "BCH", "Boston Childrens Hospital", "Boston Childrens Hospital", 1, null, "category", null), - new Facet("narnia", "Narnia", "Narnia", "Narnia", 1, null, "category", null) - ) - ), - new FacetCategory( - "data_source", "Data Source", "What does this data relate to (image, questionnaire...)", - List.of( - new Facet("imaging", "Imaging", "Data derived from an image", "Data derived from an image", 1, null, "data_source", null), - new Facet("questionnaire", "questionnaire", "Data derived from a questionnaire", "Data derived from a questionnaire", 1, null, "data_source", null), - new Facet("lab_test", "Lab Test", "Data derived from a lab test", "Data derived from a lab test", 1, null, "data_source", null) - ) - ) - ); + void shouldGetFacetWithChildren() { + Optional actual = subject.getFacet("study_ids_dataset_ids", "phs001963"); + Facet expected = new Facet("phs001963", "DEMENTIA-SEQ", null, null, null, List.of( + new Facet("NEST_1", "My Nested Facet 1", null, null, null, List.of(), "study_ids_dataset_ids", null), + new Facet("NEST_2", "My Nested Facet 2", null, null, null, List.of(), "study_ids_dataset_ids", null) + ), "study_ids_dataset_ids", null); + + Assertions.assertTrue(actual.isPresent()); + Assertions.assertEquals(expected, actual.get()); } @Test void shouldGetFacet() { Optional actual = subject.getFacet("study_ids_dataset_ids", "phs000007"); - Optional expected = Optional.of(new Facet("phs000007", "FHS", null, "Framingham Heart Study", null, null, "study_ids_dataset_ids", null)); + Optional expected = Optional.of(new Facet("phs000007", "FHS", null, "Framingham Heart Study", null, List.of(), "study_ids_dataset_ids", null)); Assertions.assertEquals(expected, actual); } diff --git a/src/test/resources/seed.sql b/src/test/resources/seed.sql index db1b91f..f811557 100644 --- a/src/test/resources/seed.sql +++ b/src/test/resources/seed.sql @@ -608,6 +608,8 @@ COPY public.facet (facet_id, facet_category_id, name, display, description, pare 31 1 phs002808 nuMoM2b \N \N 32 1 phs003566 SPRINT \N \N 33 1 phs001963 DEMENTIA-SEQ \N \N +55 1 NEST_1 My Nested Facet 1 \N 33 +56 1 NEST_2 My Nested Facet 2 \N 33 19 2 gad_7 Generalized Anxiety Disorder Assessment (GAD-7) \N \N 18 2 taps_tool NIDA CTN Common Data Elements = TAPS Tool \N \N \.